Desktop 6: About window
The About window of a desktop app: icon, name, version and build with a copy button, links, open-source acknowledgements and Check for updates. Small and centered on macOS, a dialog with OK on Windows.
// Ballmac UI: Desktop 6. https://ui.ballmac.com/blocks/desktop-6
"use client"
import * as React from "react"
import { Check, ChevronLeft, Copy, Info, Sparkles } from "lucide-react"
import { AppWindow, AppWindowContent, AppWindowTitleBar } from "@/components/ballmac/app-window"
import { Button, buttonVariants } from "@/components/ballmac/button"
import { useFeel, type FeelId } from "@/lib/ballmac/feel"
import { cn } from "@/lib/utils"
type Desktop6Acknowledgement = { name: string; license: string; href?: string }
type Desktop6Props = Omit<React.ComponentProps<"div">, "children"> & {
/** Which platform the window handles like. Defaults to the page's feel. */
feel?: FeelId
/** Draw the window frame. Turn off inside a real Tauri or Electron window that already has one. */
frame?: boolean
appName?: string
version?: string
/** The build number, shown in brackets after the version as macOS does. */
build?: string
/** The app icon. A generated one is drawn when you leave it out. */
icon?: React.ReactNode
/** One line about the app. */
tagline?: string
copyright?: string
/** Links under the version: website, privacy policy, support. */
links?: { label: string; href: string }[]
/** Open-source software the app includes, listed under Acknowledgements. */
acknowledgements?: Desktop6Acknowledgement[]
/** Called by "Check for updates". Leave it out to hide the button. */
onCheckForUpdates?: () => void
/** Called by OK (Windows) and the close button. */
onClose?: () => void
}
const sampleAck: Desktop6Acknowledgement[] = [
{ name: "Tauri", license: "MIT or Apache-2.0", href: "https://github.com/tauri-apps/tauri" },
{ name: "React", license: "MIT", href: "https://github.com/facebook/react" },
{ name: "Radix UI", license: "MIT", href: "https://github.com/radix-ui/primitives" },
{ name: "Lucide", license: "ISC", href: "https://github.com/lucide-icons/lucide" },
{ name: "Ballmac UI", license: "MIT", href: "https://ui.ballmac.com" },
]
function AppIcon({ icon, className }: { icon?: React.ReactNode; className?: string }) {
return (
<span
aria-hidden="true"
className={cn(
"from-primary to-primary/70 text-primary-foreground flex shrink-0 items-center justify-center rounded-[22%] bg-linear-to-b shadow-[0_2px_6px_rgb(0_0_0/0.18),inset_0_1px_0_rgb(255_255_255/0.25)] [&_svg]:size-1/2",
className
)}
>
{icon ?? <Sparkles />}
</span>
)
}
/**
* The About window: icon, name, version and build (with a button that copies them for a support email), copyright, links,
* acknowledgements for the open-source software inside, and Check for updates. Small and centered on macOS, a dialog with OK on Windows.
*/
function Desktop6({
feel: feelProp,
frame = true,
appName = "Acme",
version = "2.4.1",
build = "241",
icon,
tagline = "Notes that stay out of your way.",
copyright = "© 2026 Acme, Inc. All rights reserved.",
links = [
{ label: "Website", href: "#" },
{ label: "Privacy", href: "#" },
{ label: "Support", href: "#" },
],
acknowledgements = sampleAck,
onCheckForUpdates,
onClose,
className,
...props
}: Desktop6Props) {
const page = useFeel().feel
const feel = feelProp ?? page
const win = feel === "windows"
const mac = feel === "macos"
const id = React.useId()
const [view, setView] = React.useState<"about" | "acknowledgements">("about")
const [copied, setCopied] = React.useState(false)
const versionText = build ? `Version ${version} (${build})` : `Version ${version}`
React.useEffect(() => {
if (!copied) return
const t = setTimeout(() => setCopied(false), 1600)
return () => clearTimeout(t)
}, [copied])
const copy = async () => {
try {
await navigator.clipboard?.writeText(`${appName} ${versionText}`)
setCopied(true)
} catch {}
}
const t = (macText: string, other: string) => (mac ? macText : other)
const about = (
<div className={cn("flex flex-1 flex-col gap-4 p-6", mac ? "items-center text-center" : "")}>
<div className={cn("flex gap-4", mac ? "flex-col items-center" : "items-center")}>
<AppIcon icon={icon} className={mac ? "size-20" : "size-14"} />
<div>
<h2 id={`${id}-title`} className={cn("font-semibold", mac ? "text-base" : "text-lg")}>
{appName}
</h2>
<div className={cn("mt-0.5 flex items-center gap-1", mac && "justify-center")}>
<p className="text-muted-foreground text-xs tabular-nums select-text">{versionText}</p>
<Button variant="ghost" size="icon-sm" className="size-6 [&_svg]:size-3" onClick={copy} aria-label={copied ? "Version copied" : "Copy version"}>
{copied ? <Check aria-hidden="true" /> : <Copy aria-hidden="true" />}
</Button>
</div>
</div>
</div>
{tagline ? <p className="text-[13px] text-pretty">{tagline}</p> : null}
{links.length ? (
<nav aria-label={`About ${appName}`} className={cn("flex flex-wrap gap-x-4 gap-y-1", mac && "justify-center")}>
{links.map((l) => (
<a key={l.label} href={l.href} target="_blank" rel="noreferrer" className="text-primary rounded-sm text-[13px] underline-offset-4 outline-none hover:underline focus-visible:ring-[3px] focus-visible:ring-ring/50">
{l.label}
</a>
))}
</nav>
) : null}
<div className="flex-1" />
<div className={cn("flex flex-wrap items-center gap-2", mac ? "justify-center" : "")}>
{acknowledgements.length ? (
<Button variant="outline" size="sm" onClick={() => setView("acknowledgements")}>
{t("Acknowledgements", "Third-party notices")}
</Button>
) : null}
{onCheckForUpdates ? (
<Button variant="outline" size="sm" onClick={onCheckForUpdates}>
{t("Check for Updates…", "Check for updates")}
</Button>
) : null}
{win ? (
<Button size="sm" className="ms-auto min-w-20" onClick={onClose}>
OK
</Button>
) : null}
</div>
{copyright ? <p className="text-muted-foreground text-[11px]">{copyright}</p> : null}
<p aria-live="polite" className="sr-only">
{copied ? "Version copied" : ""}
</p>
</div>
)
const acks = (
<div className="flex min-h-0 flex-1 flex-col gap-3 p-4">
<div className="flex items-center gap-2">
<Button variant="ghost" size="sm" onClick={() => setView("about")} className="ps-1.5">
<ChevronLeft aria-hidden="true" className="rtl:rotate-180" />
Back
</Button>
<h2 id={`${id}-title`} className="text-sm font-semibold">
{t("Acknowledgements", "Third-party notices")}
</h2>
</div>
<p className="text-muted-foreground px-1 text-xs text-pretty">{appName} is built with open-source software. Thank you to everyone who works on it.</p>
<ul tabIndex={0} aria-label={t("Acknowledgements", "Third-party notices")} className="bg-background min-h-0 flex-1 divide-y overflow-y-auto rounded-md border outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50">
{acknowledgements.map((a) => (
<li key={a.name} className="flex items-center justify-between gap-3 px-3 py-2 text-[13px]">
{a.href ? (
<a href={a.href} target="_blank" rel="noreferrer" className={cn(buttonVariants({ variant: "link" }), "h-auto px-0 text-[13px] font-medium")}>
{a.name}
</a>
) : (
<span className="font-medium">{a.name}</span>
)}
<span className="text-muted-foreground text-xs">{a.license}</span>
</li>
))}
</ul>
</div>
)
const content = view === "about" ? about : acks
if (!frame)
return (
<div data-slot="desktop-6" data-feel={feelProp} role="region" aria-labelledby={`${id}-title`} className={cn("flex flex-col", className)} {...props}>
{content}
</div>
)
return (
<AppWindow
data-slot="desktop-6"
feel={feel}
onClose={onClose}
role="region"
aria-labelledby={`${id}-title`}
className={cn(mac ? "h-[24rem] w-full max-w-[19rem]" : "h-[22rem] w-full max-w-md", className)}
{...props}
>
<AppWindowTitleBar title={win ? `About ${appName}` : ""} icon={<Info />} className="border-b-0" />
<AppWindowContent className="flex flex-col">{content}</AppWindowContent>
</AppWindow>
)
}
export { Desktop6, type Desktop6Props, type Desktop6Acknowledgement }Installation
$ pnpm dlx shadcn@latest add @ballmac/desktop-6Install the dependencies.
$ pnpm add lucide-reactAdd the Ballmac items it builds on.
$ pnpm dlx shadcn@latest add @ballmac/app-window @ballmac/button @ballmac/feel-presetsCopy the source into your project.
components/ballmac/blocks/desktop-6/desktop-6.tsx// Ballmac UI: Desktop 6. https://ui.ballmac.com/blocks/desktop-6 "use client" import * as React from "react" import { Check, ChevronLeft, Copy, Info, Sparkles } from "lucide-react" import { AppWindow, AppWindowContent, AppWindowTitleBar } from "@/components/ballmac/app-window" import { Button, buttonVariants } from "@/components/ballmac/button" import { useFeel, type FeelId } from "@/lib/ballmac/feel" import { cn } from "@/lib/utils" type Desktop6Acknowledgement = { name: string; license: string; href?: string } type Desktop6Props = Omit<React.ComponentProps<"div">, "children"> & { /** Which platform the window handles like. Defaults to the page's feel. */ feel?: FeelId /** Draw the window frame. Turn off inside a real Tauri or Electron window that already has one. */ frame?: boolean appName?: string version?: string /** The build number, shown in brackets after the version as macOS does. */ build?: string /** The app icon. A generated one is drawn when you leave it out. */ icon?: React.ReactNode /** One line about the app. */ tagline?: string copyright?: string /** Links under the version: website, privacy policy, support. */ links?: { label: string; href: string }[] /** Open-source software the app includes, listed under Acknowledgements. */ acknowledgements?: Desktop6Acknowledgement[] /** Called by "Check for updates". Leave it out to hide the button. */ onCheckForUpdates?: () => void /** Called by OK (Windows) and the close button. */ onClose?: () => void } const sampleAck: Desktop6Acknowledgement[] = [ { name: "Tauri", license: "MIT or Apache-2.0", href: "https://github.com/tauri-apps/tauri" }, { name: "React", license: "MIT", href: "https://github.com/facebook/react" }, { name: "Radix UI", license: "MIT", href: "https://github.com/radix-ui/primitives" }, { name: "Lucide", license: "ISC", href: "https://github.com/lucide-icons/lucide" }, { name: "Ballmac UI", license: "MIT", href: "https://ui.ballmac.com" }, ] function AppIcon({ icon, className }: { icon?: React.ReactNode; className?: string }) { return ( <span aria-hidden="true" className={cn( "from-primary to-primary/70 text-primary-foreground flex shrink-0 items-center justify-center rounded-[22%] bg-linear-to-b shadow-[0_2px_6px_rgb(0_0_0/0.18),inset_0_1px_0_rgb(255_255_255/0.25)] [&_svg]:size-1/2", className )} > {icon ?? <Sparkles />} </span> ) } /** * The About window: icon, name, version and build (with a button that copies them for a support email), copyright, links, * acknowledgements for the open-source software inside, and Check for updates. Small and centered on macOS, a dialog with OK on Windows. */ function Desktop6({ feel: feelProp, frame = true, appName = "Acme", version = "2.4.1", build = "241", icon, tagline = "Notes that stay out of your way.", copyright = "© 2026 Acme, Inc. All rights reserved.", links = [ { label: "Website", href: "#" }, { label: "Privacy", href: "#" }, { label: "Support", href: "#" }, ], acknowledgements = sampleAck, onCheckForUpdates, onClose, className, ...props }: Desktop6Props) { const page = useFeel().feel const feel = feelProp ?? page const win = feel === "windows" const mac = feel === "macos" const id = React.useId() const [view, setView] = React.useState<"about" | "acknowledgements">("about") const [copied, setCopied] = React.useState(false) const versionText = build ? `Version ${version} (${build})` : `Version ${version}` React.useEffect(() => { if (!copied) return const t = setTimeout(() => setCopied(false), 1600) return () => clearTimeout(t) }, [copied]) const copy = async () => { try { await navigator.clipboard?.writeText(`${appName} ${versionText}`) setCopied(true) } catch {} } const t = (macText: string, other: string) => (mac ? macText : other) const about = ( <div className={cn("flex flex-1 flex-col gap-4 p-6", mac ? "items-center text-center" : "")}> <div className={cn("flex gap-4", mac ? "flex-col items-center" : "items-center")}> <AppIcon icon={icon} className={mac ? "size-20" : "size-14"} /> <div> <h2 id={`${id}-title`} className={cn("font-semibold", mac ? "text-base" : "text-lg")}> {appName} </h2> <div className={cn("mt-0.5 flex items-center gap-1", mac && "justify-center")}> <p className="text-muted-foreground text-xs tabular-nums select-text">{versionText}</p> <Button variant="ghost" size="icon-sm" className="size-6 [&_svg]:size-3" onClick={copy} aria-label={copied ? "Version copied" : "Copy version"}> {copied ? <Check aria-hidden="true" /> : <Copy aria-hidden="true" />} </Button> </div> </div> </div> {tagline ? <p className="text-[13px] text-pretty">{tagline}</p> : null} {links.length ? ( <nav aria-label={`About ${appName}`} className={cn("flex flex-wrap gap-x-4 gap-y-1", mac && "justify-center")}> {links.map((l) => ( <a key={l.label} href={l.href} target="_blank" rel="noreferrer" className="text-primary rounded-sm text-[13px] underline-offset-4 outline-none hover:underline focus-visible:ring-[3px] focus-visible:ring-ring/50"> {l.label} </a> ))} </nav> ) : null} <div className="flex-1" /> <div className={cn("flex flex-wrap items-center gap-2", mac ? "justify-center" : "")}> {acknowledgements.length ? ( <Button variant="outline" size="sm" onClick={() => setView("acknowledgements")}> {t("Acknowledgements", "Third-party notices")} </Button> ) : null} {onCheckForUpdates ? ( <Button variant="outline" size="sm" onClick={onCheckForUpdates}> {t("Check for Updates…", "Check for updates")} </Button> ) : null} {win ? ( <Button size="sm" className="ms-auto min-w-20" onClick={onClose}> OK </Button> ) : null} </div> {copyright ? <p className="text-muted-foreground text-[11px]">{copyright}</p> : null} <p aria-live="polite" className="sr-only"> {copied ? "Version copied" : ""} </p> </div> ) const acks = ( <div className="flex min-h-0 flex-1 flex-col gap-3 p-4"> <div className="flex items-center gap-2"> <Button variant="ghost" size="sm" onClick={() => setView("about")} className="ps-1.5"> <ChevronLeft aria-hidden="true" className="rtl:rotate-180" /> Back </Button> <h2 id={`${id}-title`} className="text-sm font-semibold"> {t("Acknowledgements", "Third-party notices")} </h2> </div> <p className="text-muted-foreground px-1 text-xs text-pretty">{appName} is built with open-source software. Thank you to everyone who works on it.</p> <ul tabIndex={0} aria-label={t("Acknowledgements", "Third-party notices")} className="bg-background min-h-0 flex-1 divide-y overflow-y-auto rounded-md border outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50"> {acknowledgements.map((a) => ( <li key={a.name} className="flex items-center justify-between gap-3 px-3 py-2 text-[13px]"> {a.href ? ( <a href={a.href} target="_blank" rel="noreferrer" className={cn(buttonVariants({ variant: "link" }), "h-auto px-0 text-[13px] font-medium")}> {a.name} </a> ) : ( <span className="font-medium">{a.name}</span> )} <span className="text-muted-foreground text-xs">{a.license}</span> </li> ))} </ul> </div> ) const content = view === "about" ? about : acks if (!frame) return ( <div data-slot="desktop-6" data-feel={feelProp} role="region" aria-labelledby={`${id}-title`} className={cn("flex flex-col", className)} {...props}> {content} </div> ) return ( <AppWindow data-slot="desktop-6" feel={feel} onClose={onClose} role="region" aria-labelledby={`${id}-title`} className={cn(mac ? "h-[24rem] w-full max-w-[19rem]" : "h-[22rem] w-full max-w-md", className)} {...props} > <AppWindowTitleBar title={win ? `About ${appName}` : ""} icon={<Info />} className="border-b-0" /> <AppWindowContent className="flex flex-col">{content}</AppWindowContent> </AppWindow> ) } export { Desktop6, type Desktop6Props, type Desktop6Acknowledgement }Update the import paths to match your project setup.
Usage
"use client"
import { Desktop6 } from "@/components/ballmac/blocks/desktop-6/desktop-6"
export default function Desktop6Demo() {
return <Desktop6 feel="macos" onCheckForUpdates={() => {}} />
}States
This block has no states. Static content: nothing to load, submit or fail.
Quality scorecard
Every applicable check passes
Measured in Chromium 153.0.8010.12 on October 19, 2026, on version 1.0.0, across 2 examples.
Passed: Accessibility scan
No serious or critical issues in light and dark, on a phone and a desktop (axe-core 4.13.0, WCAG 2.1 AA and best-practice rules).
Passed: Small screens
No sideways scrolling at 360, 390 or 1280 px wide.
Passed: Reduced motion
No endless animation to begin with.
Passed: Right to left
Mirrors correctly in Arabic, Hebrew, Persian and Urdu layouts (8% of elements off the mirror, limit 15%).
Passed: Clean console
No console errors or React hydration errors while loading.
Passed: Keyboard
13 tab stops, each showing a visible change when it takes focus. 2 documented keys.
Not applicable: Loading, empty and error
Static content: nothing to load, submit or fail.
Passed: Automated tests
Covered by desktop-6.test.tsx.
For information: Footprint
3.0 KB of source (gzip) in 1 file. Installs lucide-react.
Automated checks find a subset of problems and do not replace testing with a screen reader and a keyboard in your own app. What they miss.
Dependencies
- npm
- lucide-react
Use with AI
An About window. <Desktop6 appName version build tagline copyright links={[{ label, href }]} acknowledgements={[{ name, license, href }]} onCheckForUpdates onClose />. Acknowledgements open in the same window with a Back button; the copy button puts "Acme Version 2.4.1 (241)" on the clipboard. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Desktop 6: About window (@ballmac/desktop-6) to this project with the shadcn MCP, then use it where it fits.
Use it for
- The About item in a Tauri or Electron app's app menu
- Crediting the open-source licences a desktop app ships with
Not for
- A company About page on a website (use team-1 or a marketing block)
Registry JSON: https://ui.ballmac.com/r/desktop-6.json
Free to use in personal and commercial projects.
More desktop app blocks
Desktop 1: preferences window
The Settings window of a Tauri or Electron app: toolbar tabs on macOS, a side nav with setting cards on Windows. Launch at login, menu bar, appearance, a global shortcut recorder and update settings that apply as they change.
Desktop 2: menu bar and tray panel
The panel of a menu bar app on macOS or a tray flyout on Windows: a clipboard history with search, pins, a pause switch, keyboard selection and Preferences and Quit, with loading, empty and error states.
Desktop 3: software update
The update window of a desktop app, the whole cycle: checking, a new version with release notes, downloading with progress, ready to relaunch, up to date and failed. Buttons follow each platform's order and wording.
Desktop 4: licence activation
Licence activation for a paid desktop app: a trial meter, a key field that cleans up pasted keys, activation that shows why a key failed, the licensed view with deactivate, and a trial-ended mode.
Ballmac UI Pro
Need the whole screen, not one piece?
150 premium blocks (dashboards, settings, calendars, mail, shops, onboarding), a private shadcn registry and Figma tokens. Blocks that show data come with loading, empty and error states built in.
Not ready? Try three Pro blocks free
Leave your email and the blocks appear right here, ready to copy. Yours to use in any project.