Disclaimer banner
Slim supplement disclaimer band with an expandable full statement.
componentbanneranydisclaimer
$
npx shadcn@latest add @storekit/disclaimer-bannerdisclaimer-banner
Live and interactive. Switch the brand in the frame header to see the same source under another token file.
Installation
$
npx shadcn@latest add @storekit/disclaimer-banner$
pnpm dlx shadcn@latest add @storekit/disclaimer-banner$
yarn dlx shadcn@latest add @storekit/disclaimer-banner$
bunx --bun shadcn@latest add @storekit/disclaimer-bannerSource
components/storekit/compliance/disclaimer-banner.tsx
58 lines
"use client"
import { useId, useState } from "react"
import { AnimatePresence, motion, useReducedMotion } from "motion/react"
import { ChevronDown, Info } from "lucide-react"
import { cn } from "@/components/storekit/lib/cn"
/* Slim bordered disclaimer band. "Read more" expands the full statement
in place; the summary stays visible so the disclaimer is never hidden. */
export function DisclaimerBanner({
summary = "These statements have not been evaluated by the FDA.",
details = "These statements have not been evaluated by the Food and Drug Administration. Our products are dietary supplements and are not intended to diagnose, treat, cure, or prevent any disease. Talk to your doctor before use if you are pregnant, nursing, taking medication or have a medical condition. Keep out of reach of children.",
className,
}: {
summary?: string
details?: string
className?: string
}) {
const reduce = useReducedMotion()
const [open, setOpen] = useState(false)
const id = useId()
return (
<aside aria-label="Disclaimer" data-sk-section="compliance" data-sk-variant="disclaimer-banner" className={cn("rounded-sk-card border border-sk-line bg-sk-surface text-sk-ink", className)}>
<div className="flex min-h-11 items-start gap-3 py-1 pl-4 pr-1 sm:items-center">
<Info size={16} aria-hidden className="mt-3 shrink-0 text-sk-muted sm:mt-0" />
<div className="flex min-w-0 flex-1 flex-col sm:flex-row sm:items-center sm:gap-3">
<p className="flex-1 pt-2.5 text-[13px] font-medium leading-[1.5] sm:py-2.5">{summary}</p>
<button
type="button"
aria-expanded={open}
aria-controls={id}
onClick={() => setOpen((o) => !o)}
className="-ml-3 inline-flex h-11 shrink-0 items-center gap-1 self-start rounded-sk-btn px-3 text-[13px] text-sk-muted outline-none transition-colors duration-200 hover:text-sk-ink focus-visible:ring-2 focus-visible:ring-sk-accent focus-visible:ring-offset-2 focus-visible:ring-offset-sk-surface sm:ml-0 sm:self-auto"
>
{open ? "Show less" : "Read more"}
<ChevronDown size={14} aria-hidden className={cn("transition-transform duration-200", open && "rotate-180")} />
</button>
</div>
</div>
<AnimatePresence initial={false}>
{open ? (
<motion.div
id={id}
initial={reduce ? { opacity: 0 } : { height: 0, opacity: 0 }}
animate={reduce ? { opacity: 1 } : { height: "auto", opacity: 1 }}
exit={reduce ? { opacity: 0 } : { height: 0, opacity: 0 }}
transition={{ duration: 0.35, ease: [0.16, 1, 0.3, 1] }}
className="overflow-hidden"
>
<p className="border-t border-sk-line px-4 py-3 pl-11 text-[13px] leading-[1.5] text-sk-muted">{details}</p>
</motion.div>
) : null}
</AnimatePresence>
</aside>
)
}
Usage
Import from "@/components/storekit/compliance/disclaimer-banner" and pass your product data. The demo above exercises the full prop surface with sample values.