Agents
SourceBadge
Linked source badge with an automatic same-origin favicon, derived hostname, and resilient fallback.
Composition
Linked source
The badge supplies its favicon automatically; SourceFavicon can also be used independently.
- <SourceBadge>
- <SourceFavicon />
Installation
First install and activate one skin. Core deliberately contains no visual token defaults.
This agent installs from the source-badge registry. Install the bundle with the command above, or inspect the source below.
npx shadcn@latest add https://control-ui.dev/r/source-badge.jsonUsage
import { SourceBadge } from "@/components/control-ui/source-badge";export function Example({ href, title, favicon }: { href: string; title?: string; favicon?: string }) { return ( <SourceBadge href={href} faviconSrc={favicon} target="_blank"> {title} </SourceBadge> );}Dependencies
Raw code
This agent’s owned source and private support files
"use client";import { Globe } from "lucide-react";import type { ComponentProps, CSSProperties } from "react";import type { BadgeKnobStyle } from "@/components/control-ui/knob-contracts/badge-knobs";import type { SourceBadgeKnobStyle } from "@/components/control-ui/knob-contracts/source-badge-knobs";import { cn } from "@/components/control-ui/lib/cn";import { Avatar, AvatarFallback, AvatarImage } from "@/components/control-ui/ui/avatar";import { Badge } from "@/components/control-ui/ui/badge";export type SourceBadgeProps = Omit<ComponentProps<"a">, "href" | "style"> & { faviconSrc?: string | false; href: string; style?: CSSProperties & SourceBadgeKnobStyle & BadgeKnobStyle;};export function sourceHostname(href: string) { try { return new URL(href).hostname.replace(/^www\./, "") || href; } catch { return href; }}export function sourceFaviconHref(href: string) { try { const source = new URL(href); if (source.protocol !== "https:" && source.protocol !== "http:") return undefined; return new URL("/favicon.ico", source.origin).href; } catch { return undefined; }}export type SourceFaviconProps = Omit<ComponentProps<typeof Avatar>, "children" | "style"> & { faviconSrc?: string | false; href: string; imageProps?: Omit<ComponentProps<typeof AvatarImage>, "src">; fallbackProps?: ComponentProps<typeof AvatarFallback>; style?: CSSProperties & SourceBadgeKnobStyle;};export function SourceFavicon({ faviconSrc, href, imageProps, fallbackProps, className, ...props }: SourceFaviconProps) { const resolvedFaviconSrc = faviconSrc === false ? undefined : (faviconSrc ?? sourceFaviconHref(href)); return ( <Avatar data-control-ui="source-badge" data-source-badge-favicon="" data-slot="root" {...props} className={className}> {resolvedFaviconSrc ? <AvatarImage src={resolvedFaviconSrc} alt="" {...imageProps} /> : null} <AvatarFallback {...fallbackProps} className={fallbackProps?.className}> <Globe aria-hidden="true" className="size-2.5" /> </AvatarFallback> </Avatar> );}export function SourceBadge({ faviconSrc, href, children, className, rel, target, ...props }: SourceBadgeProps) { const hostname = sourceHostname(href); const resolvedRel = rel ?? (target === "_blank" ? "noreferrer noopener" : undefined); return ( <Badge variant="outline" render={ <a data-source-badge="" data-control-ui="source-badge" data-slot="root" href={href} target={target} rel={resolvedRel} className={cn(className)} {...props} /> } > <SourceFavicon href={href} faviconSrc={faviconSrc} /> <span data-control-ui="source-badge" data-slot="label" className="truncate"> {children ?? hostname} </span> </Badge> );}Knobs
Typed custom properties the recipe paints with. Set one on the root — style, a utility class, or a skin — and every slot inherits it.
--cui-source-badge-* · 6 knobsHow the cascade resolves--cui-source-badge-background<color>var(--background)--cui-source-badge-foreground<color>var(--muted-foreground)--cui-source-badge-hover-background<color>oklch(from var(--muted) l c h / 0.6)--cui-source-badge-hover-foreground<color>var(--foreground)--cui-source-badge-favicon-background<color>var(--muted)--cui-source-badge-favicon-radius<length-percentage>var(--radius-sm)