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
Horizontal scroll container with CSS scroll-snap for mobile-friendly card browsing. Hides scrollbar, supports snap-to-item alignment. Used for hourly forecast cards, category browsers, story rings, product carousels, and any horizontal list that should swipe naturally on mobile. Proper role="list" with keyboard navigation.
View the full component source code below.
"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
/* ═══════════════════════════════════════════════════════════════
HORIZONTAL SCROLL — Layer 2 Primitive
Snap-scrolling horizontal container for cards.
✅ TOKENS ✅ ARIA ✅ LOADING ✅ TOUCH
═══════════════════════════════════════════════════════════════ */
interface HorizontalScrollProps {
children: React.ReactNode
/** Snap alignment per item */
snap?: "start" | "center" | "none"
/** Gap between items */
gap?: "sm" | "md" | "lg"
/** Padding at edges */
padded?: boolean
/** Loading state — shows shimmer placeholders */
loading?: boolean
/** Number of skeleton items when loading */
skeletonCount?: number
/** Skeleton item width class */
skeletonWidth?: string
/** Accessible label */
ariaLabel?: string
className?: string
}
const gapMap = { sm: "gap-2", md: "gap-3", lg: "gap-4" }
export function HorizontalScroll({
children, snap = "start", gap = "md", padded = true,
loading = false, skeletonCount = 5, skeletonWidth = "w-28",
ariaLabel = "Scrollable list", className,
}: HorizontalScrollProps) {
if (loading) {
return (
<div data-slot="horizontal-scroll" data-portal="https://design.nyuchi.com/components/horizontal-scroll" data-loading role="list" aria-label={ariaLabel}
className={cn("flex overflow-hidden", gapMap[gap], padded && "px-4", className)}>
{Array.from({ length: skeletonCount }).map((_, i) => (
<div key={i} role="listitem" className={cn("shrink-0 h-20 rounded-[var(--radius-md,12px)] bg-muted animate-pulse", skeletonWidth)} />
))}
</div>
)
}
return (
<div data-slot="horizontal-scroll" role="list" aria-label={ariaLabel}
className={cn(
"flex overflow-x-auto scrollbar-none",
snap !== "none" && "snap-x snap-mandatory",
gapMap[gap],
padded && "px-4",
className
)}>
{React.Children.map(children, (child) => (
<div role="listitem" className={cn("shrink-0", snap === "start" && "snap-start", snap === "center" && "snap-center")}>
{child}
</div>
))}
</div>
)
}
npx shadcn@latest add https://mzizi.dev/api/v1/ui/horizontal-scrollFetch this component's metadata and source code from the registry API.
/api/v1/ui/horizontal-scroll