nyuchimzizi
Mzizi — an open-architecture project of the Bundu Foundation, operated and developed by Nyuchi. Built on the Five African Minerals palette.
Built by Nyuchi Africav4.0.39
Universal branded group/community card with dynamic mineral accent based on the parent app. Shows group name, member count, avatar strip, activity indicator, privacy level, topic tags, and join CTA. Used across Circles (communities), Campfire (group chats), BushTrade (seller groups), Nhimbe (event communities), and any app with group-based features.
View the full component source code below.
"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
import { useNyuchiHarness } from "@/lib/harness"
/* ═══════════════════════════════════════════════════════════════
NYUCHI GROUP CARD — Brand Component (Pre-Wired)
Mineral: Terracotta (community, Ubuntu, connection).
✅ HARNESS ✅ TOKENS ✅ STRICT MINERAL RULES ✅ TOUCH 48px+
═══════════════════════════════════════════════════════════════ */
interface CircleMember { name: string; avatarUrl?: string }
interface NyuchiGroupCardProps {
name: string
description?: string
memberCount: number
members?: CircleMember[]
topics?: string[]
joined?: boolean
activity?: "quiet" | "active" | "buzzing"
privacy?: "open" | "closed" | "secret"
coverUrl?: string
onJoin?: () => void
onClick?: () => void
className?: string
}
const activityDot = { quiet: "bg-muted-foreground/40", active: "bg-[var(--color-malachite,#64FFDA)]", buzzing: "bg-[var(--color-gold,#FFD740)]" } as const
export function NyuchiGroupCard({ loading = false, name, description, memberCount, members = [], topics = [], joined = false, activity = "active", privacy = "open", coverUrl, onJoin, onClick, className }: NyuchiGroupCardProps) {
const { log, motion, LiveRegion } = useNyuchiHarness("group-card")
const animStyle = React.useMemo(() => motion.prefersReduced ? {} : { animation: `nyuchi-fade-slide-up ${motion.enterDuration}ms ${motion.enterEasing} both` }, [motion])
if (loading) return (<div data-slot="nyuchi-group-card" data-portal="https://design.nyuchi.com/components/nyuchi-group-card" data-loading role="article" className="animate-pulse rounded-[var(--radius-lg,14px)] bg-card p-4 ring-1 ring-foreground/10 space-y-3"><div className="flex items-center gap-3"><div className="size-12 shrink-0 rounded-full bg-muted" /><div className="flex-1 space-y-1.5"><div className="h-3.5 w-1/2 rounded bg-muted" /><div className="h-2.5 w-1/3 rounded bg-muted" /></div></div><div className="flex gap-2"><div className="h-2.5 w-20 rounded bg-muted" /><div className="h-2.5 w-16 rounded bg-muted" /></div></div>)
if (loading) return (<div data-slot="nyuchi-group-card" data-loading role="article" className="animate-pulse rounded-[var(--radius-lg,14px)] bg-card ring-1 ring-foreground/10 overflow-hidden"><div className="h-20 bg-muted" /><div className="p-4 space-y-2"><div className="h-4 w-1/2 rounded bg-muted" /><div className="h-2.5 w-2/3 rounded bg-muted" /><div className="flex gap-1 mt-2">{Array.from({length:4}).map((_,i)=>(<div key={i} className="size-6 rounded-full bg-muted" />))}</div></div></div>)
return (
<div data-slot="nyuchi-group-card" role="article" onClick={onClick} className={cn("group/circle bg-card text-card-foreground overflow-hidden rounded-[var(--radius-lg,14px)] border border-border focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--color-primary,#00B0FF)] transition-shadow hover:shadow-md", onClick && "cursor-pointer", className)}>
<div className="relative h-20 bg-gradient-to-br from-[var(--brand-accent,var(--color-terracotta,#D4A574))]/20 to-[var(--brand-accent,var(--color-terracotta,#D4A574))]/5" style={coverUrl ? { backgroundImage: `url(${coverUrl})`, backgroundSize: "cover", backgroundPosition: "center" } : undefined}>
<span className="absolute right-3 top-3 rounded-full bg-background/80 px-2 py-0.5 text-[10px] font-medium text-muted-foreground backdrop-blur-sm">{privacy === "secret" ? "🔒 Secret" : privacy === "closed" ? "🔐 Closed" : "🌍 Open"}</span>
</div>
<div className="p-4 space-y-3">
<div><h3 className="truncate text-sm font-semibold text-foreground" style={{ fontFamily: "var(--font-serif)" }}>{name}</h3>
<div className="mt-0.5 flex items-center gap-1.5 text-[11px] text-muted-foreground"><span className={cn("size-1.5 rounded-full", activityDot[activity])} /><span>{activity}</span><span>·</span><span>{memberCount.toLocaleString()} members</span></div></div>
{description && <p className="line-clamp-2 text-xs text-muted-foreground leading-relaxed">{description}</p>}
{members.length > 0 && <div className="flex items-center -space-x-2">{members.slice(0, 5).map((m, i) => (<div key={i} className="size-7 rounded-full border-2 border-card bg-muted flex items-center justify-center text-[9px] font-bold text-muted-foreground overflow-hidden">{m.avatarUrl ? <img src={m.avatarUrl} alt={m.name} className="size-full object-cover" /> : m.name.charAt(0).toUpperCase()}</div>))}{memberCount > 5 && <div className="flex size-7 items-center justify-center rounded-full border-2 border-card bg-muted text-[9px] font-medium text-muted-foreground">+{Math.min(memberCount - 5, 99)}</div>}</div>}
{topics.length > 0 && <div className="flex flex-wrap gap-1">{topics.slice(0, 3).map(t => (<span key={t} className="rounded-full bg-[var(--brand-accent,var(--color-terracotta,#D4A574))]/10 px-2 py-0.5 text-[10px] font-medium text-[var(--brand-accent,var(--color-terracotta,#D4A574))]">{t}</span>))}</div>}
{onJoin && <button onClick={e => { e.stopPropagation(); onJoin() }} className={cn("flex h-12 w-full items-center justify-center rounded-full text-[13px] font-medium transition-opacity hover:opacity-80", joined ? "bg-muted text-foreground border border-border" : "bg-[var(--brand-accent,var(--color-terracotta,#D4A574))] text-[#0A0A0A]")}>{joined ? "Joined" : "Join Circle"}</button>}
</div>
</div>
)
}
npx shadcn@latest add https://mzizi.dev/api/v1/ui/nyuchi-group-cardFetch this component's metadata and source code from the registry API.
/api/v1/ui/nyuchi-group-card