Skip to content
Merged
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
108 changes: 2 additions & 106 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ The divider is fully keyboard accessible:
- **Shift + Arrow**: Resize by larger step (default: 50px)
- **Home**: Minimize left/top pane
- **End**: Maximize left/top pane
- **Escape**: Restore pane sizes to initial state
- **Tab**: Navigate between dividers

## API Reference
Expand Down Expand Up @@ -342,112 +343,7 @@ A subtle single-pixel divider:

## Tailwind CSS & shadcn/ui

React Split Pane works seamlessly with Tailwind CSS and shadcn/ui. The component uses plain CSS and inline styles (no CSS-in-JS), so there are no conflicts with utility-first frameworks.

### Using Tailwind Classes

Apply Tailwind classes directly via `className` props. Skip importing the default stylesheet for full Tailwind control:

```tsx
import { SplitPane, Pane } from 'react-split-pane';
// Don't import 'react-split-pane/styles.css' if using Tailwind

<SplitPane
className="h-screen w-full"
dividerClassName="bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 transition-colors"
>
<Pane defaultSize="300px" className="bg-white dark:bg-gray-900 p-4">
<Sidebar />
</Pane>
<Pane className="bg-gray-50 dark:bg-gray-800 p-4">
<MainContent />
</Pane>
</SplitPane>
```

### shadcn/ui Integration

Use shadcn's CSS variables and utilities for consistent theming:

```tsx
import { SplitPane, Pane } from 'react-split-pane';

<SplitPane
className="h-full w-full"
dividerClassName="bg-border hover:bg-accent transition-colors"
>
<Pane defaultSize="280px" className="bg-background border-r">
<Sidebar />
</Pane>
<Pane className="bg-muted/50">
<MainContent />
</Pane>
</SplitPane>
```

### Custom Divider with shadcn

Create a themed divider component using shadcn's `cn` utility:

```tsx
import { cn } from '@/lib/utils';
import type { DividerProps } from 'react-split-pane';

function ThemedDivider({ direction, isDragging, disabled, ...props }: DividerProps) {
return (
<div
className={cn(
'flex items-center justify-center transition-colors',
'bg-border hover:bg-accent focus:outline-none focus:ring-2 focus:ring-ring',
direction === 'horizontal'
? 'w-1 cursor-col-resize'
: 'h-1 cursor-row-resize',
isDragging && 'bg-primary',
disabled && 'cursor-not-allowed opacity-50'
)}
{...props}
/>
);
}

<SplitPane divider={ThemedDivider}>
<Pane>Left</Pane>
<Pane>Right</Pane>
</SplitPane>
```

### CSS Variables with Tailwind

Override the default CSS variables in your `globals.css` to match your Tailwind theme:

```css
/* globals.css */
@layer base {
:root {
--split-pane-divider-size: 4px;
--split-pane-divider-color: theme('colors.gray.200');
--split-pane-divider-color-hover: theme('colors.gray.300');
--split-pane-focus-color: theme('colors.blue.500');
}

.dark {
--split-pane-divider-color: theme('colors.gray.700');
--split-pane-divider-color-hover: theme('colors.gray.600');
}
}
```

Or with shadcn/ui CSS variables:

```css
@layer base {
:root {
--split-pane-divider-color: hsl(var(--border));
--split-pane-divider-color-hover: hsl(var(--accent));
--split-pane-focus-color: hsl(var(--ring));
}
}
```
React Split Pane works seamlessly with Tailwind CSS and shadcn/ui. See [TAILWIND.md](./TAILWIND.md) for detailed integration examples including custom dividers and CSS variable overrides.

## Migration from v0.1.x

Expand Down
108 changes: 108 additions & 0 deletions TAILWIND.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Tailwind CSS & shadcn/ui Integration

React Split Pane works seamlessly with Tailwind CSS and shadcn/ui. The component uses plain CSS and inline styles (no CSS-in-JS), so there are no conflicts with utility-first frameworks.

## Using Tailwind Classes

Apply Tailwind classes directly via `className` props. Skip importing the default stylesheet for full Tailwind control:

```tsx
import { SplitPane, Pane } from 'react-split-pane';
// Don't import 'react-split-pane/styles.css' if using Tailwind

<SplitPane
className="h-screen w-full"
dividerClassName="bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 transition-colors"
>
<Pane defaultSize="300px" className="bg-white dark:bg-gray-900 p-4">
<Sidebar />
</Pane>
<Pane className="bg-gray-50 dark:bg-gray-800 p-4">
<MainContent />
</Pane>
</SplitPane>
```

## shadcn/ui Integration

Use shadcn's CSS variables and utilities for consistent theming:

```tsx
import { SplitPane, Pane } from 'react-split-pane';

<SplitPane
className="h-full w-full"
dividerClassName="bg-border hover:bg-accent transition-colors"
>
<Pane defaultSize="280px" className="bg-background border-r">
<Sidebar />
</Pane>
<Pane className="bg-muted/50">
<MainContent />
</Pane>
</SplitPane>
```

## Custom Divider with shadcn

Create a themed divider component using shadcn's `cn` utility:

```tsx
import { cn } from '@/lib/utils';
import type { DividerProps } from 'react-split-pane';

function ThemedDivider({ direction, isDragging, disabled, ...props }: DividerProps) {
return (
<div
className={cn(
'flex items-center justify-center transition-colors',
'bg-border hover:bg-accent focus:outline-none focus:ring-2 focus:ring-ring',
direction === 'horizontal'
? 'w-1 cursor-col-resize'
: 'h-1 cursor-row-resize',
isDragging && 'bg-primary',
disabled && 'cursor-not-allowed opacity-50'
)}
{...props}
/>
);
}

<SplitPane divider={ThemedDivider}>
<Pane>Left</Pane>
<Pane>Right</Pane>
</SplitPane>
```

## CSS Variables with Tailwind

Override the default CSS variables in your `globals.css` to match your Tailwind theme:

```css
/* globals.css */
@layer base {
:root {
--split-pane-divider-size: 4px;
--split-pane-divider-color: theme('colors.gray.200');
--split-pane-divider-color-hover: theme('colors.gray.300');
--split-pane-focus-color: theme('colors.blue.500');
}

.dark {
--split-pane-divider-color: theme('colors.gray.700');
--split-pane-divider-color-hover: theme('colors.gray.600');
}
}
```

Or with shadcn/ui CSS variables:

```css
@layer base {
:root {
--split-pane-divider-color: hsl(var(--border));
--split-pane-divider-color-hover: hsl(var(--accent));
--split-pane-focus-color: hsl(var(--ring));
}
}
```
9 changes: 4 additions & 5 deletions src/components/Divider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
getDividerLabel,
getKeyboardInstructions,
} from '../utils/accessibility';
import { cn } from '../utils/classNames';

const DEFAULT_CLASSNAME = 'split-pane-divider';

Expand Down Expand Up @@ -79,14 +80,12 @@ export function Divider(props: DividerProps) {
...style,
};

const combinedClassName = [
const combinedClassName = cn(
DEFAULT_CLASSNAME,
direction,
isDragging && 'dragging',
className,
]
.filter(Boolean)
.join(' ');
className
);

const label = getDividerLabel(index, direction);
const instructions = getKeyboardInstructions(direction);
Expand Down
5 changes: 2 additions & 3 deletions src/components/Pane.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { CSSProperties } from 'react';
import { forwardRef } from 'react';
import type { PaneProps } from '../types';
import { cn } from '../utils/classNames';

const DEFAULT_CLASSNAME = 'split-pane-pane';

Expand Down Expand Up @@ -54,9 +55,7 @@ export const Pane = forwardRef<HTMLDivElement, PaneProps>(
...style,
};

const combinedClassName = [DEFAULT_CLASSNAME, className]
.filter(Boolean)
.join(' ');
const combinedClassName = cn(DEFAULT_CLASSNAME, className);

return (
<div
Expand Down
5 changes: 2 additions & 3 deletions src/components/SplitPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Divider } from './Divider';
import { useResizer } from '../hooks/useResizer';
import { useKeyboardResize } from '../hooks/useKeyboardResize';
import { convertToPixels, distributeSizes } from '../utils/calculations';
import { cn } from '../utils/classNames';

const DEFAULT_CLASSNAME = 'split-pane';
const MIN_PANES = 2;
Expand Down Expand Up @@ -262,9 +263,7 @@ export function SplitPane(props: SplitPaneProps) {
...style,
};

const containerClassName = [DEFAULT_CLASSNAME, direction, className]
.filter(Boolean)
.join(' ');
const containerClassName = cn(DEFAULT_CLASSNAME, direction, className);

// Render panes and dividers
const renderChildren = () => {
Expand Down
19 changes: 11 additions & 8 deletions src/hooks/useResizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,17 @@ export function useResizer(options: UseResizerOptions): UseResizerResult {
const onResizeEndRef = useRef(onResizeEnd);
onResizeEndRef.current = onResizeEnd;

// Update current sizes when prop sizes change (only when not dragging)
if (
!isDragging &&
sizes !== currentSizes &&
JSON.stringify(sizes) !== JSON.stringify(currentSizes)
) {
setCurrentSizes(sizes);
}
// Sync sizes from props when not dragging (React 19 compatible)
const sizesRef = useRef(sizes);
useEffect(() => {
if (
!isDragging &&
JSON.stringify(sizes) !== JSON.stringify(sizesRef.current)
) {
sizesRef.current = sizes;
setCurrentSizes(sizes);
}
}, [sizes, isDragging]);

// Track mounted state for RAF cleanup
useEffect(() => {
Expand Down
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export type {
Direction,
Size,
ResizeEvent,
PaneState,
} from './types';

// Re-export hooks for advanced usage
Expand Down
Loading