# Features 8

> Command-style capability picker with search and arrow keys — blog-12 content-highlight adapted for features.

A shadcn/ui block from the [@nusaiba](https://nusaiba.dev) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/nusaiba/features-8).

- Page, with a live preview: https://shadcn.exchange/r/nusaiba/features-8
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/nusaiba/features-8-light-dbb681d03fced0bf.png
- Registry item: https://nusaiba.dev/r/base-nova/features-8.json
- Categories: features
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://nusaiba.dev/r/base-nova/features-8.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `motion`
- Registry items: `@nusaiba/foundation`

## Files

### `registry/blocks/features/8/features-index-section.tsx`

```tsx
"use client";

import { useCallback, useEffect, useState } from "react";
import { CommandIcon, SearchIcon } from "lucide-react";
import { motion, useReducedMotion } from "motion/react";
import { defaultSpring, motionViewport } from "@/lib/motion-tokens";
import { cn } from "@/lib/utils";
import { SectionShell, ContentRail } from "@/components/layout-contract";

const capabilities = [
	{
		id: "motion-docs",
		tag: "docs",
		title: "Motion pattern docs",
		excerpt: "40 live patterns with spring tokens, triggers, and block links.",
	},
	{
		id: "cli-install",
		tag: "cli",
		title: "Shadcn CLI install",
		excerpt: "npx shadcn add @nusaiba/block-name copies source into your repo.",
	},
	{
		id: "pro-blocks",
		tag: "pro",
		title: "Pro block catalog",
		excerpt: "Token-gated depth sections with commercial license.",
	},
	{
		id: "copy-prompt",
		tag: "ai",
		title: "Copy prompt per block",
		excerpt: "AI-ready install handoff on every block page.",
	},
	{
		id: "preview-live",
		tag: "preview",
		title: "Live block preview",
		excerpt: "Inspect motion before anything lands in your codebase.",
	},
	{
		id: "token-auth",
		tag: "auth",
		title: "Registry token",
		excerpt: "Private installs for Pro blocks — no subscription on solo use.",
	},
];

export function FeaturesWorkflowIndexSection() {
	const reduceMotion = useReducedMotion();
	const [activeIndex, setActiveIndex] = useState(0);
	const [query, setQuery] = useState("");

	const filtered = capabilities.filter((item) => {
		const haystack = `${item.title} ${item.tag} ${item.excerpt}`.toLowerCase();
		return haystack.includes(query.toLowerCase());
	});

	const activeItem = filtered[activeIndex];

	const move = useCallback(
		(delta: number) => {
			if (filtered.length === 0) return;
			setActiveIndex((current) => (current + delta + filtered.length) % filtered.length);
		},
		[filtered.length],
	);

	useEffect(() => {
		setActiveIndex(0);
	}, [query]);

	useEffect(() => {
		function onKeyDown(event: KeyboardEvent) {
			if (event.key === "ArrowDown") {
				event.preventDefault();
				move(1);
			}
			if (event.key === "ArrowUp") {
				event.preventDefault();
				move(-1);
			}
		}

		window.addEventListener("keydown", onKeyDown);
		return () => window.removeEventListener("keydown", onKeyDown);
	}, [move]);

	return (
		<SectionShell spacingMode="section" className="bg-background">
			<ContentRail maxWidth="max-w-2xl" className="space-y-8">
				<div className="max-w-xl space-y-2">
					<p className="font-medium text-primary text-xs uppercase tracking-[0.22em]">
						Index
					</p>
					<h2 className="text-balance font-medium text-2xl tracking-tight md:text-4xl">
						Capability lookup
					</h2>
					<p className="text-pretty text-muted-foreground text-sm md:text-base">
						Command-style picker for developer audiences — pairs with blog-12 and
						pricing-3 on the same landing.
					</p>
				</div>

				<motion.div
					className="overflow-hidden rounded-[var(--radius-md)] border border-border bg-background shadow-[0_1px_2px_rgba(0,0,0,0.04)] dark:shadow-[0_1px_2px_rgba(0,0,0,0.2)]"
					initial={reduceMotion ? false : { opacity: 0, y: 12, filter: "blur(4px)" }}
					transition={defaultSpring}
					viewport={motionViewport}
					whileInView={reduceMotion ? undefined : { opacity: 1, y: 0, filter: "blur(0px)" }}
				>
					<div className="flex items-center gap-3 border-b border-border px-4 py-3">
						<SearchIcon className="size-4 text-muted-foreground" />
						<input
							className="min-w-0 flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
							onChange={(event) => setQuery(event.target.value)}
							placeholder="Filter workflows…"
							type="search"
							value={query}
						/>
						<kbd className="hidden items-center gap-0.5 rounded-[var(--radius-sm)] border border-border bg-muted/40 px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground sm:inline-flex">
							<CommandIcon className="size-2.5" />K
						</kbd>
					</div>

					<ul className="max-h-[20rem] divide-y divide-border overflow-y-auto p-1">
						{filtered.map((item, index) => {
							const isActive = index === activeIndex;

							return (
								<li key={item.id}>
									<button
										className={cn(
											"flex w-full items-start gap-3 rounded-[var(--radius-sm)] px-3 py-3 text-left transition-[background-color,transform] active:scale-[0.98]",
											"after:absolute after:top-1/2 after:left-1/2 after:size-10 after:-translate-1/2",
											isActive
												? "bg-muted/50 shadow-[inset_0_0_0_1px_rgba(0,0,0,0.06)] dark:shadow-[inset_0_0_0_1px_rgba(255,255,255,0.08)]"
												: "hover:bg-muted/25",
										)}
										onClick={() => setActiveIndex(index)}
										onMouseEnter={() => setActiveIndex(index)}
										type="button"
									>
										<span className="mt-0.5 font-mono text-muted-foreground text-xs tabular-nums">
											{String(capabilities.indexOf(item) + 1).padStart(2, "0")}
										</span>
										<span className="min-w-0 flex-1">
											<span className="flex flex-wrap items-center gap-2">
												<span className="font-medium text-sm">{item.title}</span>
												<span className="rounded-[var(--radius-sm)] bg-muted px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground uppercase">
													{item.tag}
												</span>
											</span>
											<span className="mt-1 block text-pretty text-muted-foreground text-xs leading-relaxed">
												{item.excerpt}
											</span>
										</span>
									</button>
								</li>
							);
						})}
					</ul>

					{activeItem ? (
						<motion.div
							animate={{ opacity: 1, y: 0 }}
							className="border-t border-border bg-muted/20 px-4 py-3"
							initial={{ opacity: 0, y: 6 }}
							key={activeItem.id}
							transition={{ type: "spring", duration: 0.3, bounce: 0 }}
						>
							<p className="text-pretty text-muted-foreground text-xs">
								<span className="font-medium text-foreground">{activeItem.title}</span>
								{" — "}
								{activeItem.excerpt}
							</p>
						</motion.div>
					) : null}
				</motion.div>

				<p className="font-mono text-[10px] text-muted-foreground uppercase tracking-[0.14em]">
					↑ ↓ navigate rows
				</p>
			</ContentRail>
		</SectionShell>
	);
}
```
