Badge
A customizable badge component with various styles, sizes, and optional animations.
Introduction
The ZyfloBadge component is a versatile and customizable badge that can be easily integrated into your Next.js project. It offers a range of styling options, icon support, and optional animations.
Add The Component
Add the following component to your project in the /components/zyflo directory:
"use client"
import React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
import { motion, Variants } from "framer-motion"
import {
zyfloFadeInFromBottomVariants,
zyfloFadeInFromTopVariants
} from "@/zyflo.config"
import { z } from "zod"
import {
getAutoContrastClassName,
getCSSVariable,
areColorsCompatible
} from "@/lib/utils"
export const PossibleZyfloBadgeVariant = [
"default",
"secondary",
"destructive",
"outline",
"success",
"warning",
"info",
"light",
"gradient"
] as const
export type ZyfloBadgeVariant = (typeof PossibleZyfloBadgeVariant)[number]
export const PossibleZyfloBadgeSize = ["sm", "md", "lg"] as const
export type ZyfloBadgeSize = (typeof PossibleZyfloBadgeSize)[number]
export const badgeVariants = cva(
"inline-flex items-center rounded-full zyflo-transition focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 border-2",
{
variants: {
variant: {
default:
"border-primary/30 bg-primary/30 hover:border-primary/80 dark:border-primary/40 dark:bg-primary/30 dark:hover:border-primary/70 text-gray-950 dark:text-gray-50",
secondary:
"bg-secondary hover:bg-secondary/90 dark:bg-secondary dark:hover:bg-secondary/90 border-muted-foreground/20 hover:border-muted-foreground/40 text-gray-950 dark:text-gray-50",
destructive:
"border-red-300 bg-red-200 hover:border-red-400 dark:border-red-800 dark:bg-red-950 dark:hover:border-red-700 text-red-950 dark:text-red-50",
outline:
"border-gray-300 bg-transparent hover:bg-background/10 dark:border-gray-800 hover:border-gray-400 dark:hover:border-gray-700 text-gray-950 dark:text-gray-50",
success:
"border-emerald-300 bg-emerald-200 hover:border-emerald-400 dark:border-emerald-800 dark:bg-emerald-950 dark:hover:border-emerald-700 text-emerald-950 dark:text-emerald-50",
warning:
"border-yellow-400 bg-yellow-200 hover:border-yellow-500 dark:border-yellow-800 dark:bg-yellow-950 dark:hover:border-yellow-700 text-yellow-950 dark:text-yellow-50",
info: "border-blue-300 bg-blue-200 hover:border-blue-400 dark:border-blue-800 dark:bg-blue-950 dark:hover:border-blue-700 text-blue-950 dark:text-blue-50",
light:
"border-primary/20 bg-primary/10 hover:border-primary/40 dark:border-primary/30 dark:bg-primary/20 dark:hover:border-primary/50 text-primary-700 dark:text-primary-400",
gradient:
"bg-gradient-to-r bg-[size:300%_300%] hover:bg-[position:0%_0%] bg-[position:100%_100%] from-primary to-primary via-accent border-primary/10 hover:border-primary/30"
},
size: {
sm: "px-3 py-1 text-xs",
md: "px-4 py-1.5 text-sm",
lg: "px-6 py-2 text-base"
}
},
defaultVariants: {
variant: "default",
size: "md"
}
}
)
export interface ZyfloBadgeProps
extends React.HTMLAttributes<HTMLSpanElement>,
VariantProps<typeof badgeVariants> {
icon?: React.ReactNode
iconPlacement?: "left" | "right"
disableAnimations?: boolean
variant?: ZyfloBadgeVariant
size?: ZyfloBadgeSize
}
export const ZyfloBadge = React.forwardRef<HTMLSpanElement, ZyfloBadgeProps>(
({
className,
variant,
size,
icon,
iconPlacement = "left",
disableAnimations = false,
children,
...props
}) => {
const IconWrapper = disableAnimations ? React.Fragment : motion.span
const iconAnimationProps = disableAnimations
? {}
: {
initial: { opacity: 0, scale: 0 },
animate: { opacity: 1, scale: 1 },
transition: { duration: 0.3, delay: 0.3 }
}
const primaryHSL: number[] = getCSSVariable("--primary")
.replace("%", "")
.slice(0, -1)
.split(" ")
.map(Number)
const primaryAndBlackAreCompatible = areColorsCompatible(
primaryHSL[0],
primaryHSL[1],
primaryHSL[2],
0,
0,
0
)
const primaryAndWhiteAreCompatible = areColorsCompatible(
primaryHSL[0],
primaryHSL[1],
primaryHSL[2],
100,
100,
100
)
const gradientVariantClassName = getAutoContrastClassName(
primaryAndBlackAreCompatible,
primaryAndWhiteAreCompatible
)
const contentRef = React.useRef<HTMLSpanElement>(null)
React.useEffect(() => {
if (variant === "gradient" && contentRef.current) {
console.log(gradientVariantClassName)
contentRef.current.style.color = gradientVariantClassName
}
return () => {
if (contentRef.current) {
contentRef.current.style.color = ""
}
}
}, [variant, gradientVariantClassName])
const content = (
<span
ref={contentRef}
className={cn(badgeVariants({ variant, size }), className)}
{...props}
>
{icon && iconPlacement === "left" && (
<IconWrapper {...iconAnimationProps} className="mr-2">
{icon}
</IconWrapper>
)}
<motion.span
variants={zyfloFadeInFromTopVariants as unknown as Variants}
initial="initial"
animate="animate"
viewport={{ once: true }}
custom={1}
>
{children}
</motion.span>
{icon && iconPlacement === "right" && (
<IconWrapper {...iconAnimationProps} className="ml-2">
{icon}
</IconWrapper>
)}
</span>
)
if (disableAnimations) {
return content
}
return (
<motion.div
variants={zyfloFadeInFromBottomVariants as unknown as Variants}
initial="initial"
animate="animate"
viewport={{ once: true }}
transition={{ duration: 0.3, delay: 0 }}
className="overflow-hidden"
>
{content}
</motion.div>
)
}
)
ZyfloBadge.displayName = "ZyfloBadge"
Usage
Here's a basic example of how to use the Badge component:
import { ZyfloBadge } from "@/components/zyflo/badge"
export default function MyPage() {
return (
<ZyfloBadge variant="default" size="md">
Badge Example Text
</ZyfloBadge>
)
}
Examples
Here's a comprehensive example showcasing all variants and sizes of the ZyfloBadge component:
Small Size
Medium Size
Large Size
With Icons
This example showcases all variants of the ZyfloBadge component in small, medium, and large sizes, as well as examples with icons. The preview tab displays the badges visually, while the code tab provides the corresponding JSX code for implementation.
Props
Quick Props Overview
Prop | Description |
---|---|
variant | Defines the visual style of the badge |
size | Defines the size of the badge |
icon | An icon to display within the badge |
iconPlacement | Determines the placement of the icon |
disableAnimations | If true, disables all animations |
className | Additional CSS classes |
srOnly | Text for screen readers only |
label | Accessible name for the badge |
Detailed Props Overview
The ZyfloBadge component accepts the following props:
variant
- Type: ZyfloBadgeVariant
- Default: "default"
- Possible values: "default", "secondary", "destructive", "outline", "success", "warning", "info", "light", "gradient"
Defines the visual style of the badge.
size
- Type: ZyfloBadgeSize
- Default: "md"
- Possible values: "sm", "md", "lg"
Defines the size of the badge.
icon
- Type: React.ReactNode
- Optional
An icon to display within the badge.
iconPlacement
- Type: "left" | "right"
- Default: "left"
Determines the placement of the icon within the badge.
disableAnimations
- Type: boolean
- Default: false
If set to true, disables all animations in the badge.
className
- Type: string
- Optional
Additional CSS classes to be applied to the badge.
srOnly
- Type: string
- Optional
Adds a span with the sr-only class containing the provided text. This text will be invisible on screen but readable by screen readers.
label
- Type: string
- Optional
Adds an aria-label attribute to the badge element. This provides an accessible name for the badge, which can be useful for screen readers when the visual text doesn't fully describe the badge's purpose.
Customization
The component uses Tailwind CSS classes for styling. You can customize its appearance by modifying the CSS classes in the component's source code or by passing additional classes through the className prop.
Accessibility
The component is designed with accessibility in mind:
- It uses semantic HTML elements (span) for proper structure.
- The component supports keyboard focus and includes appropriate focus styles.
- The
srOnly
prop allows you to provide additional context for screen readers without affecting the visual appearance. - The
label
prop provides a way to add an accessible name to the badge, enhancing its description for screen readers.
Notes
- The component uses Framer Motion for animations. Make sure you have Framer Motion installed in your project if you plan to use animations.
- The gradient variant automatically adjusts its text color based on the primary color to ensure proper contrast.
Contributing
If you find any issues or have suggestions for improvements, please feel free to open an issue or submit a pull request on our GitHub repository. We appreciate your contributions and are always open to collaboration.
Thank you for considering contributing to Zyflo!