Announcement
A news pill with an inset badge that nests in its curve.
Installation
$ pnpm dlx shadcn@latest add @ssntl/announcementUsage
import { Announcement, AnnouncementTag } from "@/components/ui/announcement"
<Announcement href="/news/gift-cards">
<AnnouncementTag>New</AnnouncementTag>
Gift cards are here
</Announcement>Rules
What this component expects from you, as it ships in components/ui/announcement.md next to the code. The full file adds when to use it, keyboard, the UI foundations it applies and every shared rule.
## Rules for Announcement
- **Alert + inset badge.** A pill badge 4px inside a pill, so the badge's radius is the outer radius minus the padding.
- The badge carries a word (New, Beta), and the link text says what is new.
- The whole pill is one target; the arrow moves 2px on hover and stays still with reduced motion.Code
import * as React from "react"
import { ChevronRight } from "lucide-react"
import { Slot } from "radix-ui"
import { cn } from "@/lib/utils"
// A pill that links to news ("New · Gift cards are here"). Nested corners:
// the badge is a pill inset 4px inside a pill, so its radius is the outer
// one minus the padding, by construction. The badge carries a word, and the
// whole pill is one 40px target.
function Announcement({
asChild = false,
className,
children,
...props
}: React.ComponentProps<"a"> & { asChild?: boolean }) {
const Comp = asChild ? Slot.Root : "a"
return (
<Comp
data-slot="announcement"
className={cn(
"group inline-flex min-h-10 max-w-full items-center gap-3 rounded-full bg-secondary p-1 pe-3 text-caption text-foreground",
"transition-[background-color] duration-(--duration-fast) ease-out hover:bg-neutral-200 dark:hover:bg-neutral-600",
"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring",
className
)}
{...props}
>
<Slot.Slottable>{children}</Slot.Slottable>
<ChevronRight
aria-hidden
strokeWidth={1.6}
className="size-4 shrink-0 opacity-80 transition-[translate] duration-(--duration-fast) ease-out group-hover:translate-x-0.5 motion-reduce:transition-none rtl:rotate-180"
/>
</Comp>
)
}
/** The inset badge: a word, on the card surface. */
function AnnouncementTag({
className,
...props
}: React.ComponentProps<"span">) {
return (
<span
data-slot="announcement-tag"
className={cn(
"inline-flex h-8 shrink-0 items-center rounded-full bg-card px-3 font-medium elevation-1",
className
)}
{...props}
/>
)
}
export { Announcement, AnnouncementTag }