Search

Ctrl + K

Avatar

This document outlines the steps to create Avatar component styled withTailwind CSS and using some npm dependency libraries.

Prerequisites

  • Node.js and npm installed on your machine.
  • Tailwind CSS installed in your project.
  • CVA(class-variance-authority) is a utility for managing CSS class names based on various conditions.
  • clsx is a tiny utility for constructing className strings conditionally.
  • tinycolor2 is a lightweight library helping get contrast text color based on background.
Step 1: Create helper functions
1// helpers.ts
2import { cva } from "class-variance-authority";
3import tinycolor from "tinycolor2";
4
5export const getRandomHexColor = () => {
6  return "#" + Math.floor(Math.random() * 16777215).toString(16);
7};
8
9export const getTextColor = (hex: string) => {
10  return tinycolor(hex).isDark() ? "white" : "black";
11};
12
13export const radiusVariants = cva("", {
14  variants: {
15    radius: {
16      small: "rounded-md",
17      large: "rounded-xl",
18      full: "rounded-full",
19    },
20  },
21  defaultVariants: {
22    radius: "full",
23  },
24});
25
26export const wrapperVariants = cva(
27  "overflow-hidden p-[1px] data-[bordered=true]:border-[3px] border-solid data-[disabled=true]:opacity-60",
28  {
29    variants: {
30      size: {
31        small: "w-8 h-8",
32        medium: "w-10 h-10",
33        large: "w-14 h-14",
34      },
35    },
36    defaultVariants: {
37      size: "medium",
38    },
39  },
40);
41
42export const textVariants = cva("font-bold !leading-[0]", {
43  variants: {
44    size: {
45      small: "text-lg",
46      medium: "text-xl",
47      large: "text-3xl",
48    },
49  },
50  defaultVariants: {
51    size: "medium",
52  },
53});
54
Step 2: Create Avatar component
1// avatar.component.tsx
2import { VariantProps } from "class-variance-authority";
3import clsx from "clsx";
4import { useMemo, useState } from "react";
5import {
6  getRandomHexColor,
7  getTextColor,
8  radiusVariants,
9  textVariants,
10  wrapperVariants,
11} from "./helpers";
12
13interface Props
14  extends VariantProps<typeof wrapperVariants>,
15    VariantProps<typeof radiusVariants> {
16  src?: string;
17  name: string;
18  bordered?: boolean;
19  disabled?: boolean;
20  randomFallbackColor?: boolean;
21  className?: string;
22  imageClassName?: string;
23  fallbackClassName?: string;
24}
25
26const Avatar: React.FC<AvatarProps> = ({
27  src,
28  name,
29  radius,
30  size,
31  hideImage,
32  bordered,
33  disabled,
34  randomFallbackColor,
35  className,
36  imageClassName,
37  fallbackClassName,
38}) => {
39  const [showingFallback, setShowingFallback] = useState<boolean>(!src);
40
41  const randomColor = useMemo(() => {
42    const bgColor = getRandomHexColor();
43    const textColor = getTextColor(bgColor);
44    return { bgColor, textColor };
45  }, []);
46
47  return (
48    <div
49      data-bordered={Boolean(bordered)}
50      data-disabled={Boolean(disabled)}
51      style={{
52        borderColor:
53          (hideImage || showingFallback) && randomFallbackColor
54            ? randomColor.bgColor
55            : "#a3a3a3",
56      }}
57      className={clsx(
58        wrapperVariants({ size }),
59        radiusVariants({ radius }),
60        className
61      )}
62    >
63      {hideImage || showingFallback ? (
64        <div
65          style={{
66            backgroundColor: randomFallbackColor
67              ? randomColor.bgColor
68              : "#a3a3a3",
69            color: randomFallbackColor ? randomColor.textColor : "white",
70          }}
71          className={clsx(
72            "flex h-full w-full items-center justify-center",
73            radiusVariants({ radius }),
74            fallbackClassName
75          )}
76        >
77          <span className={textVariants({ size })}>
78            {name.charAt(0).toUpperCase()}
79          </span>
80        </div>
81      ) : (
82        <img
83          src={src}
84          alt={name}
85          className={clsx(
86            "h-full w-full",
87            radiusVariants({ radius }),
88            imageClassName
89          )}
90          onError={() => setShowingFallback(true)}
91        />
92      )}
93    </div>
94  );
95};
96
97export default Avatar;
98