Skip to content

feat: Add Avatar and AvatarGroup components#88

Merged
kylengn merged 3 commits intomainfrom
feat/add-avatar-components
Nov 6, 2025
Merged

feat: Add Avatar and AvatarGroup components#88
kylengn merged 3 commits intomainfrom
feat/add-avatar-components

Conversation

@kylengn
Copy link
Contributor

@kylengn kylengn commented Nov 5, 2025

Summary by CodeRabbit

  • New Features

    • Avatar component with support for images, initials, logos, and custom content with multiple size and radius options.
    • AvatarGroup component for displaying grouped avatars with overlap and configurable overflow indicators.
    • Shadow design tokens for light-mode styling across borders and button variants.
    • New icons: ShipfoxLogo and imageAdd.
  • Documentation

    • Added comprehensive Storybook stories showcasing Avatar and AvatarGroup variants, sizes, and overflow behaviors.

@kylengn kylengn self-assigned this Nov 5, 2025
Copilot AI review requested due to automatic review settings November 5, 2025 09:40
@vercel
Copy link

vercel bot commented Nov 5, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
react-ui Ready Ready Preview Comment Nov 5, 2025 0:45am

@coderabbitai
Copy link

coderabbitai bot commented Nov 5, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Introduces a new Avatar component system with grouping and overflow support, adds light-mode shadow design tokens, implements custom icon components including a Shipfox logo, extends standard icon mappings with imageAdd, and updates export barrels across the component library.

Changes

Cohort / File(s) Summary
Configuration & Ignores
\.gitignore
Added .cursorrules entry to gitignore list.
Design Tokens
libs/react/ui/index.css
Introduced nine new light-mode CSS custom properties for shadow styles covering border and button variants (interactive, neutral, danger) with focus states.
Avatar Component
libs/react/ui/src/components/avatar/avatar.tsx
Implemented new Avatar component with Radix UI integration, supporting multiple content types (image, logo, letters, upload), size/radius variants, light theme toggle, and hover animation options. Exports Avatar, AvatarRoot, AvatarImage, AvatarFallback.
Avatar Grouping
libs/react/ui/src/components/avatar/avatar-group.tsx
Introduced AvatarGroup component for stacked avatar display with maxVisible overflow handling, adaptive sizing by variant, and z-indexed layering. Exports AvatarGroup and AvatarGroupItem type.
Avatar Stories
libs/react/ui/src/components/avatar/avatar.stories.tsx
Added comprehensive Storybook setup for Avatar and AvatarGroup with stories covering default, sizes, overflow behaviors, mixed content, and hover animation variants.
Avatar Exports
libs/react/ui/src/components/avatar/index.ts
Created barrel export file re-exporting all avatar and avatar-group modules.
Custom Icons
libs/react/ui/src/components/icon/custom/shipfox-logo.tsx, libs/react/ui/src/components/icon/custom/index.ts
Added ShipfoxLogoIcon component (88x88 SVG with customizable color) and re-exported from custom icon barrel.
Standard Icons
libs/react/ui/src/components/icon/icon.tsx
Extended iconsMap with imageAdd entry pointing to RiImageAddFill; updated IconName type and iconNames array accordingly.
Component Exports
libs/react/ui/src/components/index.ts
Added re-export for avatar component module.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Avatar component implementation (avatar.tsx): Review logic for content variant rendering, initial derivation, color handling, and CVA styling integration.
  • AvatarGroup overflow handling (avatar-group.tsx): Verify visible avatar calculation, z-index stacking, and responsive size/rounding mappings.
  • Storybook stories (avatar.stories.tsx): Confirm story metadata, render functions, and comprehensive coverage of component variants.
  • CSS shadow tokens (index.css): Validate that new custom properties align with existing design system conventions.

Poem

🐰 A burrow of avatars, grouped and stacked,
With shadows that dance in light and black,
From shipfox trails to overflow's art,
New icons join—let the components start!

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changeset: introducing two new components (Avatar and AvatarGroup) to the React UI library.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds a new Avatar component along with an AvatarGroup component to the React UI library. It includes support for various content types (letters, logo, logoPlaceholder, image, upload) with customizable sizes and radius options. Additionally, it adds a new custom ShipfoxLogoIcon and the imageAdd icon to support the avatar functionality, and introduces shadow CSS variables for button styling.

  • Adds Avatar and AvatarGroup components with multiple size and radius variants
  • Introduces ShipfoxLogoIcon custom icon component
  • Adds imageAdd icon (RiImageAddFill) to the icon map
  • Adds shadow CSS variables for button styling

Reviewed Changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
libs/react/ui/src/components/index.ts Exports the new avatar components
libs/react/ui/src/components/icon/icon.tsx Adds RiImageAddFill icon to the icon map
libs/react/ui/src/components/icon/custom/shipfox-logo.tsx Implements custom ShipfoxLogoIcon component
libs/react/ui/src/components/icon/custom/index.ts Exports the ShipfoxLogoIcon
libs/react/ui/src/components/avatar/index.ts Exports avatar and avatar-group components
libs/react/ui/src/components/avatar/avatar.tsx Main Avatar component implementation with multiple content types
libs/react/ui/src/components/avatar/avatar.stories.tsx Storybook stories demonstrating Avatar and AvatarGroup usage
libs/react/ui/src/components/avatar/avatar-group.tsx AvatarGroup component for displaying multiple avatars with overflow handling
libs/react/ui/index.css Adds shadow CSS variables for button styling
.gitignore Adds .cursorrules to the ignore list

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (3)
libs/react/ui/src/components/icon/custom/shipfox-logo.tsx (1)

4-6: Consider alignment with Remixicon's styling patterns.

The custom color prop diverges from Remixicon's standard approach, where icons use currentColor and are styled via CSS. While the custom prop provides explicit control, it may be less intuitive for users familiar with styling other icons in the system via CSS properties.

If you prefer consistency with Remixicon patterns, you could use currentColor instead:

-type ShipfoxLogoProps = ComponentProps<RemixiconComponentType> & {
-  color?: string;
-};
+type ShipfoxLogoProps = ComponentProps<RemixiconComponentType>;

Then in the component:

-export function ShipfoxLogoIcon({color = '#FF4B00', ...props}: ShipfoxLogoProps) {
+export function ShipfoxLogoIcon(props: ShipfoxLogoProps) {
   return (
     <svg
       ...
     >
       <title>Shipfox Logo</title>
       <path
         fillRule="evenodd"
         clipRule="evenodd"
         d="..."
-        fill={color}
+        fill="currentColor"
       />
     </svg>
   );
 }

Users could then set the color via CSS: <ShipfoxLogoIcon style={{color: '#FF4B00'}} /> or className.

libs/react/ui/src/components/avatar/avatar.tsx (2)

56-61: Consider handling empty fallback more explicitly.

The getInitial function returns 'L' as a default when no valid character is found. While functional, this arbitrary default could be confusing in contexts where an empty fallback is intentional.

Consider making the default more explicit or configurable:

-const getInitial = (fallbackText?: string): string => {
-  if (fallbackText) {
-    return fallbackText.trim()[0]?.toUpperCase() ?? 'L';
-  }
-  return 'L';
-};
+const getInitial = (fallbackText?: string, defaultInitial = 'L'): string => {
+  const trimmed = fallbackText?.trim();
+  return trimmed?.[0]?.toUpperCase() ?? defaultInitial;
+};

164-170: Consider making logo color configurable.

The Shipfox logo color is hardcoded to #FF4B00. While this may be intentional for brand consistency, making it configurable would improve component flexibility.

Add an optional logoColor prop:

 export type AvatarProps = ComponentProps<typeof AvatarPrimitive.Root> &
   VariantProps<typeof avatarVariants> & {
     content?: AvatarContent;
     src?: string;
     alt?: string;
     fallback?: string;
     lightTheme?: boolean;
     animateOnHover?: boolean;
+    logoColor?: string;
   };

Then use it in the logo rendering:

     if (content === 'logo') {
       return (
         <AvatarFallback className={cn(innerClassName, 'p-[15%]')}>
-          <ShipfoxLogoIcon color="#FF4B00" className="h-full w-full p-2" />
+          <ShipfoxLogoIcon color={logoColor ?? "#FF4B00"} className="h-full w-full p-2" />
         </AvatarFallback>
       );
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e1346d2 and 464c140.

📒 Files selected for processing (10)
  • .gitignore (1 hunks)
  • libs/react/ui/index.css (1 hunks)
  • libs/react/ui/src/components/avatar/avatar-group.tsx (1 hunks)
  • libs/react/ui/src/components/avatar/avatar.stories.tsx (1 hunks)
  • libs/react/ui/src/components/avatar/avatar.tsx (1 hunks)
  • libs/react/ui/src/components/avatar/index.ts (1 hunks)
  • libs/react/ui/src/components/icon/custom/index.ts (1 hunks)
  • libs/react/ui/src/components/icon/custom/shipfox-logo.tsx (1 hunks)
  • libs/react/ui/src/components/icon/icon.tsx (2 hunks)
  • libs/react/ui/src/components/index.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*

⚙️ CodeRabbit configuration file

We handle errors at the edge of our applications in most cases. Do not recommend to add error handling around every single function. We prefer them to bubble up and be handled at upper layers.

Files:

  • libs/react/ui/src/components/avatar/index.ts
  • libs/react/ui/src/components/icon/custom/shipfox-logo.tsx
  • libs/react/ui/src/components/avatar/avatar.tsx
  • libs/react/ui/src/components/icon/custom/index.ts
  • libs/react/ui/src/components/index.ts
  • libs/react/ui/src/components/avatar/avatar.stories.tsx
  • libs/react/ui/index.css
  • libs/react/ui/src/components/avatar/avatar-group.tsx
  • libs/react/ui/src/components/icon/icon.tsx
🧬 Code graph analysis (3)
libs/react/ui/src/components/avatar/avatar.tsx (2)
libs/react/ui/src/components/icon/custom/shipfox-logo.tsx (1)
  • ShipfoxLogoIcon (8-27)
libs/react/ui/src/components/icon/icon.tsx (1)
  • Icon (47-50)
libs/react/ui/src/components/avatar/avatar.stories.tsx (2)
libs/react/ui/src/components/avatar/avatar.tsx (1)
  • Avatar (108-234)
libs/react/ui/src/components/avatar/avatar-group.tsx (2)
  • AvatarGroup (56-108)
  • AvatarGroupItem (47-47)
libs/react/ui/src/components/avatar/avatar-group.tsx (1)
libs/react/ui/src/components/avatar/avatar.tsx (2)
  • AvatarProps (98-106)
  • Avatar (108-234)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Agent
🔇 Additional comments (11)
.gitignore (1)

142-144: Clean addition of tool-specific ignore rule.

The addition of .cursorrules to the ignore list is appropriate for local Cursor IDE configuration, following the standard practice of not committing development tool-specific settings. The formatting and organization are consistent with the rest of the file.

libs/react/ui/index.css (1)

293-323: Light-mode shadow tokens properly implemented with consistent dark-mode pairing.

The new shadow design tokens follow the established pattern with appropriate color adjustments for light mode (using --color-alpha-white-* variants for focus rings) compared to dark mode. All tokens are properly re-exposed in the @theme inline block for Tailwind CSS integration.

libs/react/ui/src/components/icon/custom/shipfox-logo.tsx (1)

18-18: Good accessibility practice with the title element.

Including a descriptive title improves screen reader support.

libs/react/ui/src/components/icon/custom/index.ts (1)

9-9: Export correctly integrated.

The new export follows the existing barrel export pattern and is properly placed alphabetically.

libs/react/ui/src/components/icon/icon.tsx (1)

5-5: Icon integration looks good.

The imageAdd icon is correctly imported and mapped following the established pattern. The type system will automatically include it in the IconName union.

Also applies to: 37-37

libs/react/ui/src/components/index.ts (1)

2-2: LGTM! Clean barrel export.

The avatar export is correctly added and maintains alphabetical order.

libs/react/ui/src/components/avatar/avatar.tsx (2)

224-224: Verify the aggressive hover animation translation.

The hover animation translates the avatar up by 32px (-translate-y-8), which is quite significant and could feel jarring, especially for smaller avatar sizes.

Consider testing this animation across all size variants to ensure it provides good UX. You might want to make the translation distance proportional to the avatar size:

const translateMap: Record<NonNullable<typeof size>, string> = {
  '3xs': 'hover:-translate-y-2',
  '2xs': 'hover:-translate-y-2',
  'xs': 'hover:-translate-y-3',
  'sm': 'hover:-translate-y-4',
  'md': 'hover:-translate-y-6',
  'lg': 'hover:-translate-y-8',
  'xl': 'hover:-translate-y-10',
  '2xl': 'hover:-translate-y-16',
  '3xl': 'hover:-translate-y-20',
};

198-216: LGTM! Clean icon size mapping.

The upload content implementation with size-based icon scaling is well-structured and handles all size variants appropriately.

libs/react/ui/src/components/avatar/index.ts (1)

1-2: LGTM! Standard barrel exports.

The avatar package exports are correctly structured.

libs/react/ui/src/components/avatar/avatar-group.tsx (1)

64-67: LGTM! Overflow logic is well-implemented.

The overflow calculation and badge rendering correctly handle the maxVisible prop with appropriate z-index stacking.

Also applies to: 94-105

libs/react/ui/src/components/avatar/avatar.stories.tsx (1)

93-259: LGTM! Comprehensive story coverage.

The AvatarGroup stories provide excellent coverage of all major features including sizing, overflow handling, mixed content types, and animation behaviors.

@kylengn kylengn merged commit 8be27df into main Nov 6, 2025
5 checks passed
@kylengn kylengn deleted the feat/add-avatar-components branch November 6, 2025 02:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants