Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/kokonut-labs/kokonutui/llms.txt

Use this file to discover all available pages before exploring further.

An interactive team size selector with animated avatar stack. Features smooth animations, vibration feedback, and elegant visual design.

Installation

npx shadcn@latest add @kokonutui/team-selector

Usage

import TeamSelector from "@/components/kokonutui/team-selector";

export default function Example() {
  return (
    <TeamSelector />
  );
}

Props

defaultValue
number
default:"1"
Initial team size (1-4)
onChange
(size: number) => void
Callback function triggered when team size changes. Receives the new size as parameter.
className
string
Additional CSS classes to apply to the container

Features

  • Avatar Stack: Visual representation with overlapping avatars
  • Smooth Animations: Spring-based transitions for avatar appearance/disappearance
  • Counter Animation: Sliding number animation with directional awareness
  • Vibration Feedback: Shake animation when trying to exceed limits
  • Limit Enforcement: Prevents going below 1 or above 4 team members
  • Keyboard Accessible: Full keyboard support for increment/decrement
  • Dark Mode: Fully styled for both light and dark themes

Constants

const MAX_TEAM_SIZE = 4;        // Maximum team members
const AVATAR_OVERLAP = 10;      // Pixel overlap between avatars

Animation Variants

Container Animation

container: {
  initial: { opacity: 0, y: 20 },
  animate: { opacity: 1, y: 0, duration: 0.45 }
}

Avatar Animation

avatar: {
  visible: { opacity: 1, scale: 1 },    // Spring animation
  hidden: { opacity: 0, scale: 0.6 }     // Fade + shrink
}

Vibration Animation

vibration: {
  shake: { x: [-4, 4, -4, 4, 0], duration: 0.38 }
}

Counter Animation

The counter uses direction-aware animations:
  • Increment: New number slides up from bottom
  • Decrement: New number slides down from top

Example with Handler

import TeamSelector from "@/components/kokonutui/team-selector";

export default function Example() {
  const handleTeamSizeChange = (size: number) => {
    console.log(`Team size changed to: ${size}`);
    // Update your application state
  };

  return (
    <TeamSelector 
      defaultValue={2}
      onChange={handleTeamSizeChange}
      className="my-8"
    />
  );
}

Team Member Data

The component uses predefined avatar URLs:
const TEAM_MEMBERS = [
  { id: "member-1", avatarUrl: "...", name: "Team Member 1" },
  { id: "member-2", avatarUrl: "...", name: "Team Member 2" },
  { id: "member-3", avatarUrl: "...", name: "Team Member 3" },
  { id: "member-4", avatarUrl: "...", name: "Team Member 4" },
];
All four avatars are always rendered but animated in/out based on the current team size.

Styling Details

Card Container

  • White background with subtle shadow
  • Rounded corners (rounded-2xl)
  • Border with low opacity
  • Dark mode: Semi-transparent zinc background

Avatars

  • 48px × 48px (size-12)
  • Rounded full circle
  • White border with shadow
  • -10px left margin for overlap (except first)
  • Z-index stacking (decreasing order)

Buttons

  • 36px × 36px (h-9 w-9)
  • Gradient background (white → zinc-50)
  • Border with shadow
  • Hover effects: darker border, increased shadow
  • Disabled state: reduced opacity, no pointer

Counter Display

  • Large text (text-3xl)
  • Gradient text (zinc-800 → zinc-500)
  • Bold weight
  • Small label below (“member” / “members”)

Accessibility

  • Semantic <fieldset> and <legend> elements
  • <output> element for counter with ARIA label
  • Descriptive aria-label on buttons
  • Keyboard support (Enter and Space keys)
  • Disabled state prevents invalid actions
  • Screen reader announces current team size

State Management

Internal states:
  • peopleCount: Current team size
  • isVibrating: Vibration animation trigger
  • directionRef: Tracks increment/decrement for animation direction

Dependencies

  • motion (Framer Motion with Variants type)
  • next/image
  • react (useState, useRef hooks)