# fma-ds > A personal component registry in the shadcn/ui format, built on react-aria-components: components, blocks, libs, hooks and themes copied into your project by the shadcn CLI (not an npm package). Register the namespace in components.json: ```json { "registries": { "@fma-ds": "https://ds.fmartinsandre.dev.br/r/{name}.json" } } ``` Then add items by name: `pnpm dlx shadcn@latest add @fma-ds/button`. Dependencies between items resolve through the same namespace. Everything installs into `fma-ds` folders (`components/fma-ds/`, `hooks/fma-ds/`, `lib/fma-ds/`), so it never collides with the official shadcn components. The APIs follow React Aria, not Radix: `isDisabled` instead of `disabled`, `onPress` instead of `onClick`, and no `asChild` (use `LinkButton` for a link that looks like a button). Wrap the app in React Aria's `RouterProvider` for client-side links (see the Routing guide). License: MIT, except for themes adapted from tweakcn (Apache-2.0). Each registry item states its own in meta.license; upstream notices are in https://github.com/fmartins-andre/fma-ds/blob/main/THIRD_PARTY_NOTICES.md. ## Guides ### Compatibility fma-ds is code you copy, so it runs on your project's toolchain. These are the versions it is built and tested against. | Requirement | Version | How you get it | | --- | --- | --- | | React and React DOM | `>=19.0.0` | Your project | | Tailwind CSS | `>=4.0.0` | Your project; v3 doesn't work, tokens are CSS-first | | shadcn CLI | `^4.22.1` | Run with `npx`, or as a dev dependency | | react-aria-components | `^1.22.1` | Installed by the CLI with each item | Each item lists the rest of its npm dependencies in its registry file, and the CLI installs them. Node.js only matters to the shadcn CLI, so use a version it supports. The Node and pnpm versions in this repository's `package.json` apply to working on the registry itself, not to using it. ## Project setup You need a project initialized for shadcn/ui: a `components.json` and a stylesheet with Tailwind v4. Then follow the [installation steps](/docs). ### Dark mode Dark mode is on by a `dark` class on an ancestor, usually ``. Every theme defines both palettes, so adding the class is all it takes. Your stylesheet needs this variant, which the shadcn setup adds. With it, the tokens switch by themselves: ```css @custom-variant dark (&:is(.dark *)); ``` fma-ds doesn't ship a toggle or a provider, and it doesn't read `prefers-color-scheme` by itself: which mode applies is your app's decision. ## Avoid a flash on load Set the class in an inline script in ``, before the first paint. This one follows the saved choice and falls back to the system preference: ```html ``` If your framework renders on the server, add `suppressHydrationWarning` to ``: the class is set before React hydrates. ## Add a toggle ```tsx function setMode(mode: "light" | "dark") { document.documentElement.classList.toggle("dark", mode === "dark"); localStorage.setItem("mode", mode); } ``` Portaled overlays render under ``, so keep the class on `` or they will stay in the other mode. ### Routing Links in fma-ds are react-aria links. Without a router they do a full page load, so wrap your app in React Aria's `RouterProvider` to send them through your router. It takes `navigate`, called when a link is pressed, and `useHref`, which turns a path into the real `href` (needed when the app runs under a base path). ## TanStack Router Put it in the root route, around the ``. This site runs this same code: ```tsx import { Outlet, useRouter } from "@tanstack/react-router"; import { RouterProvider } from "react-aria-components"; // Links outside the app go through the browser. const isPlainLink = (href: string) => /^([a-z][a-z\d+.-]*:|\/\/)/i.test(href); function RootComponent() { const router = useRouter(); return ( { if (isPlainLink(href)) window.location.assign(href); else void router.navigate({ href }); }} useHref={(href) => isPlainLink(href) ? href : router.buildLocation({ href } as never).href } > ); } ``` ## React Router Render it inside the router, because it uses the router's hooks: ```tsx import { Outlet, useHref, useNavigate } from "react-router"; import { RouterProvider } from "react-aria-components"; export function RootLayout() { const navigate = useNavigate(); return ( ); } ``` ## Current page `RouterProvider` handles navigation only. For active states, pass `aria-current="page"` where the component takes it, or use components that already do, such as `TabNav` and `Pagination`. ### Offline and privacy fma-ds makes no network requests at runtime, with one exception. There is no telemetry, and themes don't load fonts from a CDN. This was audited against every published item, looking for `fetch`, `XMLHttpRequest`, `sendBeacon`, WebSockets, remote imports and hard-coded URLs. ## The exception: emoji-picker `EmojiPicker` downloads its data from `cdn.jsdelivr.net` (a pinned `emojibase-data` release) the first time it opens. To avoid that, do one of these: - **`data`**: pass the emoji you already have, bundled or loaded on your server. Nothing is fetched. - **`dataUrl`**: point it at your own copy of `emojibase-data`. - **`shortcodes={false}`**: skips the optional shortcode requests. ## Fonts Themes with catalog fonts install them as `@fontsource` packages, imported into your CSS and bundled by your build. A theme using a font outside the catalog says so when installed, and loading that font is up to you. ## Images `Avatar`, `FileUpload` and similar components only load the URLs you give them. Nothing points to an external image by default. ## Offline-first apps With the emoji data handled, the components work with no connection. Component code is in your bundle, so it's cached the way the rest of your app is. ### Touch Controls meet WCAG 2.2 AA for pointer targets (2.5.8): at least 24 by 24 CSS pixels, or spaced far enough apart that a 24px circle on one doesn't touch the next. They are sized for desktop density, so an app used mostly by touch should pick larger sizes. This was measured in the browser across every Storybook story, counting the invisible hit areas below. ## Sizes | Control | Hit area | | --- | --- | | `Button` | 32px tall; `lg` is 36px, `sm` 28px, `xs` 24px | | `Input`, `Select` and other fields | 32px tall | | `Checkbox`, `Radio` | 16px box, 40 by 32px hit area | | `Slider` thumb | 12px thumb, 28px hit area | The hit area of the small controls is an `::after` pseudo-element, so give them room: a neighboring control placed inside it takes the press. ## Spaced instead of sized These stay under 24px and rely on spacing: `Carousel` dots, `CodeBlock` fold toggles, `ColorPicker` swatches and `EmojiPicker` category tabs. Links inside a sentence are exempt, as in `Breadcrumb`. ## Larger targets For touch-first screens, aim for the 44px of WCAG 2.5.5 (AAA). No size prop reaches it, so set the height where you use the component: ```tsx ``` ### Updating items The code lives in your project, so nothing updates by itself: you choose when to pull changes and which ones to keep. Items have no version numbers. Each one has a content hash, which changes whenever its published code does: ```bash curl -s https:///r/button.json | jq -r .meta.contentHash ``` Save the hash when you install an item, in a comment or a commit message, and a different value later means there is something new. The CLI rewrites imports on install, so hashing your local copy won't match. To see what changed, open the **Changelog** link on the item's page: it is the Git history of that item. ## Check before overwriting ```bash npx shadcn@latest add @fma-ds/button --dry-run npx shadcn@latest add @fma-ds/button --diff ``` `--dry-run` lists what would change, and `--diff` shows the difference for a file. Neither writes anything. ## Apply an update ```bash npx shadcn@latest add @fma-ds/button --overwrite ``` This replaces your copy, including local edits. Commit first, then review the diff in git and bring your changes back. Keeping customizations in wrapper components, instead of editing the files, makes updates much easier. ### Contributing fma-ds is a personal registry, developed in the open. Issues are welcome, and new components start from a real need in an app. ## Proposing a change 1. Open an [issue](https://github.com/fmartins-andre/fma-ds/issues) describing the need: what the app has to do, not how it should look. 2. Check that no existing item covers it. Prefer a variant or a prop over a new component. 3. Wait for agreement before opening a pull request, unless it's a small fix. ## Scope of the blocks Blocks come from what real apps needed, mostly SaaS screens such as sign-in, pricing, billing and settings. They are not a general application kit, so a block enters when an app needs it, not to complete the catalog. Components stay general. ## What a new item needs - A folder in `packages/ds/src/core//` with the component, `meta.json` and a story. - React Aria for interaction, and the existing tokens: hover, focus and selection use `accent`. - English default text, overridable through props. - Stories that show real usage, because they are the examples on this site, with `play` functions for interaction tests. - Accessible names, keyboard support and no automated accessibility violations. The generated registry files are committed with the change. Before opening a pull request, run: ```bash pnpm lint && pnpm type-check && pnpm test && pnpm --filter @fma-ds/ds build ``` ## Going deeper [`CONTRIBUTING.md`](https://github.com/fmartins-andre/fma-ds/blob/main/CONTRIBUTING.md) covers the environment, the anatomy of a component, themes and the Git workflow. ## Components ### Accordion A vertically stacked set of headers that expand to reveal or hide associated content. Supports single or multiple items open at once. - Install: `pnpm dlx shadcn@latest add @fma-ds/accordion` - Registry item: https://ds.fmartinsandre.dev.br/r/accordion.json - Docs: https://ds.fmartinsandre.dev.br/components/accordion - npm: cn@^0.5.0, lucide-react@^1.61.0, react-aria-components@^1.22.1 ```tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/fma-ds/accordion"; export function AccordionDemo() { return (
Is it accessible? Yes. It adheres to the WAI-ARIA design pattern. Is it styled? Yes. It comes with default styles that match the other components' aesthetic. Is it animated? Yes. It's animated by default via CSS height transitions.
); } ``` ### Alert A callout for important, static information — supports an icon, title, description, and an optional action, with semantic variants (destructive/info/success/warning/invert) and a `brand` variant for announcements and promos in the theme's second identity color. - Install: `pnpm dlx shadcn@latest add @fma-ds/alert` - Registry item: https://ds.fmartinsandre.dev.br/r/alert.json - Docs: https://ds.fmartinsandre.dev.br/components/alert - npm: class-variance-authority@^0.7.1, cn@^0.5.0 ```tsx import { Alert, AlertDescription, AlertTitle } from "@/components/fma-ds/alert"; export function AlertDemo() { return ( Heads up! You can add components to your app using the CLI. ); } ``` ### Alert Dialog A modal dialog that interrupts the user with important content and expects an explicit response (e.g. confirming a destructive action) — unlike Dialog, it can't be dismissed by clicking outside or pressing Escape. - Install: `pnpm dlx shadcn@latest add @fma-ds/alert-dialog` - Registry item: https://ds.fmartinsandre.dev.br/r/alert-dialog.json - Docs: https://ds.fmartinsandre.dev.br/components/alert-dialog - npm: cn@^0.5.0, react-aria-components@^1.22.1 - Registry: @fma-ds/button ```tsx import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/fma-ds/alert-dialog"; import { Button } from "@/components/fma-ds/button"; export function AlertDialogDemo() { return ( Are you absolutely sure? This action cannot be undone. This will permanently delete your account and remove your data from our servers. Cancel Continue ); } ``` ### Aspect Ratio Constrains content (typically an image or video) to a fixed width/height ratio, so it scales without distortion or layout shift. - Install: `pnpm dlx shadcn@latest add @fma-ds/aspect-ratio` - Registry item: https://ds.fmartinsandre.dev.br/r/aspect-ratio.json - Docs: https://ds.fmartinsandre.dev.br/components/aspect-ratio - npm: cn@^0.5.0 ```tsx import { AspectRatio } from "@/components/fma-ds/aspect-ratio"; export function AspectRatioDemo() { return (
Landscape
); } ``` ### Attachment A file/attachment chip for chat-style UIs — shows a name, optional upload progress or error state, and an independently clickable remove action. - Install: `pnpm dlx shadcn@latest add @fma-ds/attachment` - Registry item: https://ds.fmartinsandre.dev.br/r/attachment.json - Docs: https://ds.fmartinsandre.dev.br/components/attachment - npm: class-variance-authority@^0.7.1, cn@^0.5.0 - Registry: @fma-ds/button ```tsx import { FileTextIcon, XIcon } from "lucide-react"; import type * as React from "react"; import { Attachment, AttachmentAction, AttachmentActions, AttachmentContent, AttachmentDescription, AttachmentMedia, AttachmentTitle, } from "@/components/fma-ds/attachment"; function FileAttachment({ name = "sales-dashboard.pdf", description = "PDF · 2.4 MB", icon = , ...props }: React.ComponentProps & { name?: string; description?: string; icon?: React.ReactNode; }) { return ( {icon} {name} {description} ); } export function AttachmentDemo() { return (
); } ``` ### Avatar A user/entity image with a text or icon fallback for when the image fails to load. Composes into overlapping groups for showing multiple participants. - Install: `pnpm dlx shadcn@latest add @fma-ds/avatar` - Registry item: https://ds.fmartinsandre.dev.br/r/avatar.json - Docs: https://ds.fmartinsandre.dev.br/components/avatar - npm: cn@^0.5.0 ```tsx import { Avatar, AvatarFallback, AvatarImage } from "@/components/fma-ds/avatar"; export function AvatarDemo() { return ( CN ); } ``` ### Badge A small label for status, count, or category — inline with text or attached to another element. - **Semantic variants**: info, success, warning, destructive and invert, in solid, `-light` and `-outline` styles. - **Neutral**: `muted`/`muted-outline` for inactive or pending states. - **Brand**: `brand`, `brand-light`, `brand-outline` and `brand-dot` in the theme's second identity color, for identity labels ("New", "AI", "Pro"), never for status. - **Dots**: a `*-dot` family (primary, secondary, brand, success, warning, info, destructive, muted) that shows a colored dot before plain text, for select items and dense lists. - **Shape**: size and radius options. Can also render as a link. - Install: `pnpm dlx shadcn@latest add @fma-ds/badge` - Registry item: https://ds.fmartinsandre.dev.br/r/badge.json - Docs: https://ds.fmartinsandre.dev.br/components/badge - npm: class-variance-authority@^0.7.1, cn@^0.5.0 ```tsx import { Badge } from "@/components/fma-ds/badge"; export function BadgeDemo() { return Badge; } ``` ### Breadcrumb Shows the user's current location within a hierarchy, as a trail of links back to parent pages, with an ellipsis for collapsing long paths. - Install: `pnpm dlx shadcn@latest add @fma-ds/breadcrumb` - Registry item: https://ds.fmartinsandre.dev.br/r/breadcrumb.json - Docs: https://ds.fmartinsandre.dev.br/components/breadcrumb - npm: cn@^0.5.0, lucide-react@^1.61.0, react-aria-components@^1.22.1 ```tsx import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, } from "@/components/fma-ds/breadcrumb"; export function BreadcrumbDemo() { return ( Home Components Breadcrumb ); } ``` ### Bubble A single chat message bubble — start/end alignment for sent vs. received, several color variants, and an optional reactions pill overlaid on a corner. - Install: `pnpm dlx shadcn@latest add @fma-ds/bubble` - Registry item: https://ds.fmartinsandre.dev.br/r/bubble.json - Docs: https://ds.fmartinsandre.dev.br/components/bubble - npm: class-variance-authority@^0.7.1, cn@^0.5.0 ```tsx import { Bubble, BubbleContent } from "@/components/fma-ds/bubble"; export function BubbleDemo() { return ( Hey, how's the new design system coming along? ); } ``` ### Button Displays a button, or a component that looks like one, with a set of visual variants and sizes. Also exports LinkButton (react-aria-components' Link, same variants) for link-as-button use cases. - Install: `pnpm dlx shadcn@latest add @fma-ds/button` - Registry item: https://ds.fmartinsandre.dev.br/r/button.json - Docs: https://ds.fmartinsandre.dev.br/components/button - npm: class-variance-authority@^0.7.1, cn@^0.5.0, react-aria-components@^1.22.1 ```tsx import { Button } from "@/components/fma-ds/button"; export function ButtonDemo() { return ; } ``` ### Button Group Visually joins a row (or column) of buttons into one connected control, with shared borders and rounded end corners. Can include a separator or a plain text segment. - Install: `pnpm dlx shadcn@latest add @fma-ds/button-group` - Registry item: https://ds.fmartinsandre.dev.br/r/button-group.json - Docs: https://ds.fmartinsandre.dev.br/components/button-group - npm: class-variance-authority@^0.7.1, cn@^0.5.0 - Registry: @fma-ds/separator ```tsx import { Button } from "@/components/fma-ds/button"; import { ButtonGroup } from "@/components/fma-ds/button-group"; export function ButtonGroupDemo() { return ( ); } ``` ### Calendar A date grid for selecting a single date, multiple dates or a range. - **Navigation**: the header opens month and year grids for fast navigation. - **Granularity**: `granularity` switches the selection to whole months or years. - **Availability**: `unavailableRanges` blocks booked spans, and an already-selected value that becomes blocked travels to the nearest available date. - Install: `pnpm dlx shadcn@latest add @fma-ds/calendar` - Registry item: https://ds.fmartinsandre.dev.br/r/calendar.json - Docs: https://ds.fmartinsandre.dev.br/components/calendar - npm: @internationalized/date@^3.12.4, class-variance-authority@^0.7.1, cn@^0.5.0, lucide-react@^1.61.0, react-aria-components@^1.22.1 - Registry: @fma-ds/button, @fma-ds/select, @fma-ds/use-available-date-correction, @fma-ds/date-granularity, @fma-ds/interval-helpers, @fma-ds/types ```tsx import { Calendar } from "@/components/fma-ds/calendar"; export function CalendarDemo() { return ; } ``` ### Card A container for grouping related content, with header, title, description, action, content, and footer slots. Supports a compact size="sm" variant. - Install: `pnpm dlx shadcn@latest add @fma-ds/card` - Registry item: https://ds.fmartinsandre.dev.br/r/card.json - Docs: https://ds.fmartinsandre.dev.br/components/card - npm: cn@^0.5.0 ```tsx import { Button } from "@/components/fma-ds/button"; import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "@/components/fma-ds/card"; export function CardDemo() { return ( Project alpha Last updated 2 hours ago

Card content goes here.

); } ``` ### Carousel A carousel for cycling through slides (images, cards, or any content) with keyboard, swipe, and button navigation. Built on embla-carousel-react; `CarouselPrevious`/`CarouselNext` are `Button`s wired to the carousel API. - **Autoplay**: `autoplay` (`true` or `{ delay }`, 5000 ms by default) advances on its own and wraps to the first slide. Rotation pauses while the pointer is over the carousel or focus is inside it, and starts paused under reduced motion. - **`CarouselAutoplayToggle`**: pause/resume — required for moving content by WCAG 2.2.2; a dev warning flags its absence. - **`CarouselDots`**: one dot per slide; marks the current one and goes to a slide on press. - **Announcements**: slide changes are announced (`aria-live`) only when not rotating. The toggle and dots are not positioned; place them where they fit. Built-in labels are in English; translate them with `i18n.labels`. - Install: `pnpm dlx shadcn@latest add @fma-ds/carousel` - Registry item: https://ds.fmartinsandre.dev.br/r/carousel.json - Docs: https://ds.fmartinsandre.dev.br/components/carousel - npm: cn@^0.5.0, embla-carousel-react@^8.6.0, lucide-react@^1.61.0 - Registry: @fma-ds/button ```tsx import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, } from "@/components/fma-ds/carousel"; export function CarouselDemo() { return ( {Array.from({ length: 5 }, (_, index) => ( // biome-ignore lint/suspicious/noArrayIndexKey: fixed-length demo slides, index IS the identity
{index + 1}
))}
); } ``` ### Chart Composable chart primitives (ChartContainer, ChartTooltip, ChartLegend) wrapping Recharts, themed via CSS custom properties from a ChartConfig. Supports area, bar, line, and pie charts. - Install: `pnpm dlx shadcn@latest add @fma-ds/chart` - Registry item: https://ds.fmartinsandre.dev.br/r/chart.json - Docs: https://ds.fmartinsandre.dev.br/components/chart - npm: cn@^0.5.0, recharts@3.10.1 ```tsx import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"; import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, } from "@/components/fma-ds/chart"; const multiSeriesData = [ { month: "January", desktop: 186, mobile: 80 }, { month: "February", desktop: 305, mobile: 200 }, { month: "March", desktop: 237, mobile: 120 }, { month: "April", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "June", desktop: 214, mobile: 140 }, ]; const multiSeriesConfig = { desktop: { label: "Desktop", color: "var(--chart-1)" }, mobile: { label: "Mobile", color: "var(--chart-2)" }, } satisfies ChartConfig; export function ChartStackedAreaChart() { return ( value.slice(0, 3)} /> } /> ); } ``` ### Checkbox A control for a single on/off/indeterminate value. Pair it with an external `Label` (via `id`/`htmlFor`) laid out as a flex row, rather than passing label text as children. - **`CheckboxGroup`**: gathers several under one label and a `value` array (react-aria `CheckboxGroup`; each `Checkbox` takes a `value`). - **`CheckboxCard`**: a choice card — the whole card is the checkbox, its children are the content (an `IconTile`, `FieldContent` with `FieldTitle` and `FieldDescription`) and a square indicator sits at the end. Selected cards take a `primary` tint, hovered ones `accent`. - Install: `pnpm dlx shadcn@latest add @fma-ds/checkbox` - Registry item: https://ds.fmartinsandre.dev.br/r/checkbox.json - Docs: https://ds.fmartinsandre.dev.br/components/checkbox - npm: cn@^0.5.0, lucide-react@^1.61.0, react-aria-components@^1.22.1 - Registry: @fma-ds/field ```tsx import { Checkbox } from "@/components/fma-ds/checkbox"; import { Label } from "@/components/fma-ds/label"; export function CheckboxDemo() { return (
); } ``` ### Code Block A code block with shiki syntax highlighting, for docs and AI chat UIs. `` is complete on its own. ### Parts - **`CodeBlockHeader`**: the chrome — `CodeBlockTitle`, `CodeBlockLanguage`, `CodeBlockWrapToggle`, `CodeBlockCopyButton`, `CodeBlockDownloadButton`. - **`CodeBlockExpandButton`**: with `maxLines`. - **`CodeBlockContent`**: to place inside your own scroll container. - **`CodeBlockLineActions`**: one action group on the hovered or focused line. A pinned copy button stays put while the code scrolls. ### Highlighting shiki loads lazily on the first highlight (JavaScript regex engine, no WebAssembly) through the `code-block-highlight` lib. Pass `lines` from `highlightCode()` in a server loader, or `highlight={false}`, to never load it. Light and dark token colors switch with the theme. ### Lines - **Marking**: `showLineNumbers`/`startLine`, `highlightedLines`/`highlightedWords`/`focusedLines` (line specs like `"2-4,7"`), `diff` and `lineLevels` — or shiki `transformers`. - **`streaming`**: grows the code with a caret and stick-to-bottom, re-rendering only changed lines. - **`foldable`**: folds by indentation. - **`selectable`**: turns lines into a multi-select listbox. - Install: `pnpm dlx shadcn@latest add @fma-ds/code-block` - Registry item: https://ds.fmartinsandre.dev.br/r/code-block.json - Docs: https://ds.fmartinsandre.dev.br/components/code-block - npm: cn@^0.5.0, lucide-react@^1.61.0 - Registry: @fma-ds/button, @fma-ds/copy-button, @fma-ds/code-block-highlight ```tsx import { CodeBlock, CodeBlockCopyButton } from "@/components/fma-ds/code-block"; const CODE = `import { useMemo } from "react"; type Item = { price: number; quantity: number }; export function useTotals(items: Item[]) { return useMemo(() => { const subtotal = items.reduce((sum, item) => { return sum + item.price * item.quantity; }, 0); const tax = subtotal * 0.08; return { subtotal, tax, total: subtotal + tax }; }, [items]); }`; export function CodeBlockDemo() { return ( ); } ``` ### Collapsible Shows or hides a single section of content behind a trigger. A thin wrapper around react-aria-components' Disclosure — for several mutually-exclusive sections, use Accordion instead. - Install: `pnpm dlx shadcn@latest add @fma-ds/collapsible` - Registry item: https://ds.fmartinsandre.dev.br/r/collapsible.json - Docs: https://ds.fmartinsandre.dev.br/components/collapsible - npm: react-aria-components@^1.22.1 ```tsx import { InfoIcon } from "lucide-react"; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@/components/fma-ds/collapsible"; export function CollapsibleDemo() { return (

Can I use this in my project?

Yes. Free to use for personal and commercial projects. No attribution required.
); } ``` ### Color Picker A composable color picker: `ColorPicker` holds the color and shares it with parts laid out in any way. Without children, `ColorPicker` is a swatch that opens the default panel in a popover. ### Parts - **`ColorPickerTrigger`** + **`ColorPickerContent`**: a popover. - **`ColorPickerPanel`**: a column of controls, or a standalone inline picker. - **`ColorPickerArea`**: OKLCH chroma × lightness at the hue (the part sRGB can't show is left blank), or HSB saturation × brightness. - **`ColorPickerWheel`**: a hue ring whose children fill the square inside it. - **`ColorPickerSlider`**: any channel of OKLCH, HSL, HSB or RGB, or alpha; horizontal or vertical, painted with the colors it reaches. - **`ColorPickerFields`**: a format menu with hex or per-channel fields; `ColorPickerChannelInput` for a single channel. - **`ColorPickerInput`**: a text field taking any CSS color (Escape reverts), with a swatch that opens the popover against it. - **`ColorPickerSwatches`**: presets, Tailwind's palette by default; `ColorPickerSwatch` for one. - **Extras**: `ColorPickerValue`, `ColorPickerCopy`, `ColorPickerEyeDropper`, `ColorPickerPreview` and `ColorPickerFixContrast`. - **`useColorPicker`**: reads and sets the color from custom parts. ### Value - **Events**: `onChange` fires while dragging and `onChangeEnd` once an interaction ends. - **`format`**: what they emit — `oklch` by default, `hex`, `rgb` or `hsl`; `alpha` adds opacity controls. - **Forms**: `name` submits the value with a form (a hidden input), and a form reset restores the starting color. ### Contrast Given a background (`contrastWith`), every part shows contrast: the area hatches what fails AA and draws the AA and AAA thresholds across it, tracks hatch and mark them, presets failing AA are hatched, the preview shows the WCAG ratio, and "Fix to AA/AAA" jumps to the nearest passing lightness keeping the hue. `labels` translates every text it shows or announces. - Install: `pnpm dlx shadcn@latest add @fma-ds/color-picker` - Registry item: https://ds.fmartinsandre.dev.br/r/color-picker.json - Docs: https://ds.fmartinsandre.dev.br/components/color-picker - npm: cn@^0.5.0, culori@^4.0.2, lucide-react@^1.61.0, react-aria-components@^1.22.1 - Registry: @fma-ds/button, @fma-ds/copy-button, @fma-ds/input, @fma-ds/input-group, @fma-ds/popover, @fma-ds/select, @fma-ds/color-contrast ```tsx import { useState } from "react"; import { ColorPicker } from "@/components/fma-ds/color-picker"; export function ColorPickerDemo() { const [value, setValue] = useState("oklch(0.6 0.2 260)"); return (
{value}
); } ``` ### Combobox A searchable, filterable select combining a text input with a listbox popover. Supports single selection (ComboboxValue) or a multi-select tag/chip input (ComboboxChips + ComboboxChip). - Install: `pnpm dlx shadcn@latest add @fma-ds/combobox` - Registry item: https://ds.fmartinsandre.dev.br/r/combobox.json - Docs: https://ds.fmartinsandre.dev.br/components/combobox - npm: cn@^0.5.0, lucide-react@^1.61.0, react-aria-components@^1.22.1 - Registry: @fma-ds/button, @fma-ds/input-group ```tsx import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, } from "@/components/fma-ds/combobox"; const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"]; export function ComboboxDemo() { return ( No items found. ({ id: item, name: item }))}> {(item) => {item.name}} ); } ``` ### Command A searchable list of commands or actions, filtered live as you type. - **`CommandDialog`**: wraps it in a `Dialog` for a full command-palette (⌘K) pattern. - **Empty state**: pass `CommandEmpty` through `CommandList`'s `renderEmptyState`, not as a child — the list only accepts items, groups and separators. - Install: `pnpm dlx shadcn@latest add @fma-ds/command` - Registry item: https://ds.fmartinsandre.dev.br/r/command.json - Docs: https://ds.fmartinsandre.dev.br/components/command - npm: cn@^0.5.0, lucide-react@^1.61.0, react-aria-components@^1.22.1 - Registry: @fma-ds/dialog, @fma-ds/input-group ```tsx import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, } from "@/components/fma-ds/command"; export function CommandDemo() { return ( {/* The empty state goes through renderEmptyState: a plain child in the collection would empty the whole list. */} No results found.}> Calendar Search Emoji Calculator Profile Billing Settings ); } ``` ### Confirm Dialog A ready-made yes/no dialog on top of the `use-dialog-promise` hook. `const ask = useDialogPromise()`, render `` once, then `const result = await ask.open(data)` and check `result.status`: `confirmed`, `cancelled`, or `dismissed` by Escape. - **Content**: `title` and `description` take the opened `data` when given as functions; `media` adds an icon. - **Actions**: `tone="destructive"` marks a destructive action; `cancelLabel={null}` leaves a single action. - **`onConfirm`**: runs with the dialog open and the button pending; if it throws, the dialog stays open with `errorMessage`. For dialogs that collect a value or hold a form, render your own dialog with the same hook and call `confirm(value)`. Labels default to English; pass translations through the label props. - Install: `pnpm dlx shadcn@latest add @fma-ds/confirm-dialog` - Registry item: https://ds.fmartinsandre.dev.br/r/confirm-dialog.json - Docs: https://ds.fmartinsandre.dev.br/components/confirm-dialog - npm: cn@^0.5.0, lucide-react@^1.61.0 - Registry: @fma-ds/alert-dialog, @fma-ds/button, @fma-ds/use-dialog-promise ```tsx import { BanIcon } from "lucide-react"; import * as React from "react"; import { Button } from "@/components/fma-ds/button"; import { ConfirmDialog } from "@/components/fma-ds/confirm-dialog"; import { useDialogPromise } from "@/hooks/fma-ds/use-dialog-promise"; export function ConfirmDialogDemo() { const block = useDialogPromise<{ name: string }>(); const [status, setStatus] = React.useState("Ativo"); async function handleBlock() { const result = await block.open({ name: "Padaria Pão Quente" }); if (result.status === "confirmed") setStatus("Bloqueado"); } return (

Situação: {status}

} title={(data) => `Bloquear ${data?.name}?`} description="O cliente perde o acesso ao gestor de pedidos até ser reativado." confirmLabel="Bloquear" cancelLabel="Cancelar" />
); } ``` ### Context Menu A menu that opens on right-click (or long-press on touch) instead of a regular trigger click. Supports nested submenus, separators, and keyboard shortcut display. - Install: `pnpm dlx shadcn@latest add @fma-ds/context-menu` - Registry item: https://ds.fmartinsandre.dev.br/r/context-menu.json - Docs: https://ds.fmartinsandre.dev.br/components/context-menu - npm: class-variance-authority@^0.7.1, cn@^0.5.0, lucide-react@^1.61.0, react-aria-components@^1.22.1 ```tsx import { Pressable } from "react-aria-components"; import { ContextMenu, ContextMenuItem, ContextMenuTrigger } from "@/components/fma-ds/context-menu"; export function ContextMenuDemo() { return (
Right click here
Profile Billing Team Subscription
); } ``` ### Copy Button An icon button that copies a value too long to type (protocol, tracking code, Pix code, token) to the clipboard. - **Feedback**: the icon turns into a check, or a cross on failure, for a moment, and the outcome is announced in a separate live region. - **Labels**: translatable; an optional tooltip shows them. - **`useCopyToClipboard`**: also exported, for other triggers such as a menu item. - Install: `pnpm dlx shadcn@latest add @fma-ds/copy-button` - Registry item: https://ds.fmartinsandre.dev.br/r/copy-button.json - Docs: https://ds.fmartinsandre.dev.br/components/copy-button - npm: cn@^0.5.0, lucide-react@^1.61.0 - Registry: @fma-ds/button, @fma-ds/tooltip ```tsx import { CopyButton } from "@/components/fma-ds/copy-button"; export function CopyButtonDemo() { return ; } ``` ### Credit Card A payment card drawn from its data, to show a saved card or preview one while it's typed. - **Front**: the chip, the issuer's logo and color, the number padded with `•` and grouped by issuer (Amex 4-6-5), the holder and the expiry. - **Back**: the stripe, the signature panel and the security code. ### Props - **Data**: `number`, `name`, `expiry` (`MM/YY`, `MM/YYYY` or digits only) and `cvc` are plain strings. `*` or `x` in `number` hide digits of a saved card. - **`issuer`**: `visa`, `mastercard`, `amex`… — pass it for a saved card; by default it is detected from the number. - **`focused`**: `number`, `name`, `expiry` or `cvc` highlights the field being typed, and `cvc` turns the card over (Amex keeps its code on the front). With reduced motion it turns without the 3D spin. - **Size**: its width sets its size (`w-72` by default) and the text scales with it. - **`labels`**: translates the captions and the accessible name. The card is one `role="img"` ("Visa card ending in 4242, expires 12/28"). Logos for Visa, Mastercard, Elo, American Express, Hipercard, Diners Club, Discover and JCB; other issuers show their name on the theme gray. Issuer detection and the form's validation (number, expiry, security code) live in the `credit-card-validation` lib, usable without the card. Pair the number field with the `credit-card` mask, which follows the issuer too. - Install: `pnpm dlx shadcn@latest add @fma-ds/credit-card` - Registry item: https://ds.fmartinsandre.dev.br/r/credit-card.json - Docs: https://ds.fmartinsandre.dev.br/components/credit-card - npm: cn@^0.5.0 - Registry: @fma-ds/credit-card-validation ```tsx import { CreditCard } from "@/components/fma-ds/credit-card"; export function CreditCardDemo() { return ; } ``` ### Data Grid A data grid driven by a TanStack Table v9 instance and rendered with react-aria's `Table`, which brings cell keyboard navigation, `aria-sort`, selection, column resizing, row drag and drop and virtualization. ### Table Create the table with `useTable({ features: dataGridFeatures, ... })`. The exported features cover sorting, filtering with facets, pagination, selection, row expanding (tree rows from `getSubRows`), row and column pinning, ordering, visibility and sizing, with client-side row models. For server-side data (or to show every row), set `manualSorting`/`manualFiltering`/`manualPagination`. Column `meta` takes: - **`headerTitle`**, **`headerClassName`** and **`cellClassName`**. - **`skeleton`**: the cell shown in loading rows. - **`autoSize`**: the column takes the remaining width (not measured from content). - **`headerColSpan`**: the header spans the next visible columns on its pin side, e.g. "Quantity" over the amount and its unit. Covered headers take its accessible name and the last one keeps the resize handle. ### Parts Wrap them in `DataGrid table={table} recordCount={…}`: - **`DataGridTable`** and **`DataGridPagination`**. - **`DataGridColumnHeader`**: sort indicator, plus a menu for pinning, moving and visibility when `tableLayout` enables them. - **`DataGridColumnVisibility`**: a column visibility menu. - **`DataGridColumnFilter`**: faceted; use `filterFn: "arrHas"`. - **`DataGridRowSelect`**: a row checkbox, with `enableRowSelection`. - **`DataGridRowPin`**: pins to the top, or the bottom with `position="bottom"`. - **`DataGridRowActions`**: a row's actions as `DropdownMenuItem`s behind a menu button, named from `rowLabel`. It doesn't trigger `onRowClick`, and with no items it shows a disabled "no actions" entry. - **`DataGridRowDragHandle`**: with `onRowsReorder`. - **`DataGridRowExpand`**: shows `getSubRows` children, indented by depth — or, with `getRowCanExpand` and `DataGridTable`'s `renderExpandedRow`, a full-width details row. - **`DataGridSelectionBar`**: shows while rows are selected — the count (announced, with how many are on other pages), your bulk actions as children and "Clear selection". ### Rows - **`onRowClick`**: makes rows actionable (click or Enter). - **`onRowHover`**: fires when the pointer enters a row, e.g. to prefetch its details. - **`isLoading`**: shows skeleton rows (`meta.skeleton`) or a spinner. - **Footer**: `DataGridTable`'s `footerContent` takes footer rows (`DataGridTableFootRow` with `DataGridTableFootRowCell`, `colSpan` to span columns); `tableLayout.footerBackground` tints them. ### Infinite scroll `DataGridTable`'s `onFetchMore` fires near the end of the loaded rows (`fetchMoreScrollOffset`, in viewport heights), `isFetchingMore` shows a loading row and `hasMore={false}` stops it with `allRowsLoadedMessage`. Works with `isVirtualized`. ### Layout and styling - **`tableLayout.columnsDraggable`**: lets users drag headers to reorder columns; keyboard users use the header menu's move actions, and pinned columns stay put. - **`tableLayout`**: also takes `rowRounded`, `headerBorder` and `columnsResizeMode` (`"onEnd"` commits the width on release and draws a line at the pointer while dragging). - **`tableClassNames`**: adds classes to parts of the table (`base`, `header`, `headerSticky`, `body`, `bodyRow`, `footer`, `edgeCell`). - **Surfaces**: pinned cells and sticky headers paint `--data-grid-surface` (else `--card` inside a `Card`, else `--background`) and carry `data-pinned`/`data-last-col`. ### i18n The `i18n` prop on `DataGrid` replaces every built-in string (`{ labels: { sortAscending, rowsPerPage, paginationInfo: ({ from, to, count }) => …, goToPage: (page) => …, … } }`, defaults in `defaultDataGridI18n`). Specific props like `emptyMessage` or the pagination labels still win. - Install: `pnpm dlx shadcn@latest add @fma-ds/data-grid` - Registry item: https://ds.fmartinsandre.dev.br/r/data-grid.json - Docs: https://ds.fmartinsandre.dev.br/components/data-grid - npm: @tanstack/react-table@^9.3.1, cn@^0.5.0, lucide-react@^1.61.0, react-aria-components@^1.22.1 - Registry: @fma-ds/badge, @fma-ds/button, @fma-ds/checkbox, @fma-ds/dropdown-menu, @fma-ds/input, @fma-ds/popover, @fma-ds/select, @fma-ds/skeleton, @fma-ds/spinner ```tsx import { type ColumnDef, type ColumnFiltersState, type ColumnOrderState, type ColumnPinningState, type ColumnVisibilityState, type PaginationState, type RowPinningState, type RowSelectionState, type SortingState, type Table, useTable, } from "@tanstack/react-table"; import * as React from "react"; import { Badge } from "@/components/fma-ds/badge"; import { DataGrid, DataGridColumnHeader, DataGridContainer, type DataGridFeatures, type DataGridI18nOverrides, type DataGridLayout, DataGridPagination, DataGridRowExpand, DataGridRowSelect, DataGridTable, type DataGridTableClassNames, dataGridFeatures, } from "@/components/fma-ds/data-grid"; import { Skeleton } from "@/components/fma-ds/skeleton"; type User = { id: string; name: string; role: string; status: "Active" | "Inactive" | "Pending"; joined: string; }; const USERS: User[] = [ { id: "USR-001", name: "Alice Martin", role: "Frontend Engineer", status: "Active", joined: "2023-01-15", }, { id: "USR-002", name: "Bob Chen", role: "Product Manager", status: "Active", joined: "2022-07-08", }, { id: "USR-003", name: "Carol Smith", role: "UX Designer", status: "Inactive", joined: "2021-11-22", }, { id: "USR-004", name: "David Kim", role: "Backend Engineer", status: "Active", joined: "2023-05-03", }, { id: "USR-005", name: "Eva Brown", role: "Data Analyst", status: "Pending", joined: "2024-02-14", }, { id: "USR-006", name: "Frank Lee", role: "DevOps Engineer", status: "Active", joined: "2022-03-19", }, { id: "USR-007", name: "Grace Liu", role: "QA Engineer", status: "Inactive", joined: "2023-09-01", }, { id: "USR-008", name: "Henry Park", role: "Engineering Manager", status: "Active", joined: "2020-06-30", }, ]; type UserColumn = ColumnDef; const header = (title: string): UserColumn["header"] => ({ column }) => ; const COLUMNS: UserColumn[] = [ { accessorKey: "id", header: header("ID"), size: 110, meta: { headerTitle: "ID" } }, { accessorKey: "name", header: header("Name"), size: 180, meta: { headerTitle: "Name", autoSize: true }, }, { accessorKey: "role", header: header("Role"), size: 190, meta: { headerTitle: "Role" } }, { accessorKey: "status", header: header("Status"), size: 120, filterFn: "arrHas", cell: ({ row }) => ( {row.original.status} ), meta: { headerTitle: "Status", skeleton: }, }, { accessorKey: "joined", header: header("Joined"), size: 130, meta: { headerTitle: "Joined" } }, ]; const SELECT_COLUMN: UserColumn = { id: "select", size: 48, enableSorting: false, enableResizing: false, enableHiding: false, header: () => , cell: () => , meta: { headerTitle: "Select" }, }; type UsersDataGridProps = { data?: User[]; columns?: UserColumn[]; isLoading?: boolean; loadingMode?: "skeleton" | "spinner"; emptyMessage?: React.ReactNode; onRowClick?: (user: User) => void; layout?: DataGridLayout; selectable?: boolean; pageSize?: number; toolbar?: (table: Table) => React.ReactNode; /** Adds an expand column and a details row under expanded rows. */ expandable?: boolean; initialPinning?: ColumnPinningState; i18n?: DataGridI18nOverrides; tableClassNames?: DataGridTableClassNames; /** Footer rows for `footerContent`. */ footer?: (table: Table) => React.ReactNode; }; const EXPAND_COLUMN: UserColumn = { id: "expand", size: 48, enableSorting: false, enableResizing: false, enableHiding: false, header: () => Details, cell: ({ row }) => , meta: { headerTitle: "Details" }, }; function UsersDataGrid({ data = USERS, columns = COLUMNS, isLoading, loadingMode, emptyMessage, onRowClick, layout, selectable, pageSize = 5, toolbar, expandable, initialPinning = { start: [], end: [] }, i18n, footer, tableClassNames, }: UsersDataGridProps) { const [pagination, setPagination] = React.useState({ pageIndex: 0, pageSize }); const [sorting, setSorting] = React.useState([]); const [rowSelection, setRowSelection] = React.useState({}); const [columnFilters, setColumnFilters] = React.useState([]); const [columnVisibility, setColumnVisibility] = React.useState({}); const [columnOrder, setColumnOrder] = React.useState([]); const [columnPinning, setColumnPinning] = React.useState(initialPinning); const [rowPinning, setRowPinning] = React.useState({ top: [], bottom: [] }); const table = useTable({ features: dataGridFeatures, data, columns: [ ...(expandable ? [EXPAND_COLUMN] : []), ...(selectable ? [SELECT_COLUMN] : []), ...columns, ], getRowCanExpand: () => !!expandable, getRowId: (row: User) => row.id, state: { pagination, sorting, rowSelection, columnFilters, columnVisibility, columnOrder, columnPinning, rowPinning, }, onPaginationChange: setPagination, onSortingChange: setSorting, onRowSelectionChange: setRowSelection, onColumnFiltersChange: setColumnFilters, onColumnVisibilityChange: setColumnVisibility, onColumnOrderChange: setColumnOrder, onColumnPinningChange: setColumnPinning, onRowPinningChange: setRowPinning, enableRowSelection: selectable, keepPinnedRows: true, }); return ( {toolbar?.(table)} aria-label="Users" footerContent={footer?.(table)} renderExpandedRow={ expandable ? (row) => (

{row.original.name} has been a {row.original.role} since {row.original.joined}.

) : undefined } />
); } export function DataGridDemo() { return ; } ``` ### Date Field A segmented, keyboard-editable date input. - **Granularity**: `granularity` also accepts `"month"` and `"year"` for month- or year-only fields. - **Availability**: values blocked by `minValue`/`maxValue` or `unavailableRanges` move to the nearest available date once the field loses focus, so typing is never interrupted. - **`DateInput`**: exported for composing date pickers. - Install: `pnpm dlx shadcn@latest add @fma-ds/date-field` - Registry item: https://ds.fmartinsandre.dev.br/r/date-field.json - Docs: https://ds.fmartinsandre.dev.br/components/date-field - npm: @internationalized/date@^3.12.4, cn@^0.5.0, react-aria-components@^1.22.1 - Registry: @fma-ds/use-available-date-correction, @fma-ds/date-granularity, @fma-ds/interval-helpers, @fma-ds/types ```tsx import { DateField } from "@/components/fma-ds/date-field"; export function DateFieldDemo() { return ; } ``` ### Date Picker A typed date input paired with a calendar popover over one value, for single dates (`DatePicker`) or ranges (`DateRangePicker`). - **Granularity**: month- and year-only, as well as days. - **Availability**: `unavailableRanges` blocks dates. - **Non-modal popover**: the input stays editable while the calendar is open, and the calendar follows what is typed. - Install: `pnpm dlx shadcn@latest add @fma-ds/date-picker` - Registry item: https://ds.fmartinsandre.dev.br/r/date-picker.json - Docs: https://ds.fmartinsandre.dev.br/components/date-picker - npm: @internationalized/date@^3.12.4, cn@^0.5.0, lucide-react@^1.61.0, react-aria-components@^1.22.1 - Registry: @fma-ds/calendar, @fma-ds/date-field, @fma-ds/input-group, @fma-ds/popover, @fma-ds/date-granularity, @fma-ds/types ```tsx import { DatePicker } from "@/components/fma-ds/date-picker"; export function DatePickerDemo() { return ; } ``` ### Description List Term/value pairs on a semantic `
`: `DescriptionList` > `DescriptionItem` > `DescriptionTerm` + `DescriptionDetails`. - **`layout="inline"`** (default): each value at the end of its row, right-aligned with tabular numbers — for order summaries, totals and plan details. - **`layout="stacked"`**: the value under a smaller term, in a grid whose columns you set with `className` — for record details. - **`divided`**: draws rules between rows. - **Icons**: an icon inside `DescriptionTerm` sits before the label. - Install: `pnpm dlx shadcn@latest add @fma-ds/description-list` - Registry item: https://ds.fmartinsandre.dev.br/r/description-list.json - Docs: https://ds.fmartinsandre.dev.br/components/description-list - npm: class-variance-authority@^0.7.1, cn@^0.5.0 ```tsx import { Badge } from "@/components/fma-ds/badge"; import { DescriptionDetails, DescriptionItem, DescriptionList, DescriptionTerm, } from "@/components/fma-ds/description-list"; export function DescriptionListDemo() { return ( Valor mensal R$ 189,90 Valor anual R$ 2.278,80 Validade 12 meses ); } ``` ### Dialog A modal window layered over the page, requiring explicit action (or Escape) to dismiss. Pair with react-aria-components' DialogTrigger to wire up the trigger and open state. - Install: `pnpm dlx shadcn@latest add @fma-ds/dialog` - Registry item: https://ds.fmartinsandre.dev.br/r/dialog.json - Docs: https://ds.fmartinsandre.dev.br/components/dialog - npm: cn@^0.5.0, lucide-react@^1.61.0, react-aria-components@^1.22.1 - Registry: @fma-ds/button ```tsx import { Button } from "@/components/fma-ds/button"; import { Dialog, DialogClose, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/fma-ds/dialog"; export function DialogDemo() { return ( Are you absolutely sure? This action cannot be undone. This will permanently delete your account and remove your data from our servers. Cancel ); } ``` ### Direction Sets LTR/RTL text-direction context for descendant react-aria-components — a compatibility shim so a Radix/Base UI-style `direction` prop keeps working via react-aria's locale system. - Install: `pnpm dlx shadcn@latest add @fma-ds/direction` - Registry item: https://ds.fmartinsandre.dev.br/r/direction.json - Docs: https://ds.fmartinsandre.dev.br/components/direction - npm: react-aria-components@^1.22.1 ```tsx import { DirectionProvider } from "@/components/fma-ds/direction"; export function DirectionLeftToRight() { return (

This text and any react-aria-components descendants (menus, sliders, etc.) read their text direction from this provider.

); } ``` ### Drawer A panel that slides in from an edge of the screen, with swipe-to-dismiss and snap-point support. Uses @base-ui/react (not react-aria-components), so its API and data attributes differ from this repo's other overlays. - Install: `pnpm dlx shadcn@latest add @fma-ds/drawer` - Registry item: https://ds.fmartinsandre.dev.br/r/drawer.json - Docs: https://ds.fmartinsandre.dev.br/components/drawer - npm: @base-ui/react@^1.9.0, cn@^0.5.0 ```tsx import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger, } from "@/components/fma-ds/drawer"; export function DrawerDemo() { return ( Open Are you absolutely sure? This action cannot be undone. Submit Cancel ); } ``` ### Dropdown Menu A menu of actions or options triggered by clicking a button. Supports nested submenus, separators, labels, and a shortcut-display slot for each item. - Install: `pnpm dlx shadcn@latest add @fma-ds/dropdown-menu` - Registry item: https://ds.fmartinsandre.dev.br/r/dropdown-menu.json - Docs: https://ds.fmartinsandre.dev.br/components/dropdown-menu - npm: class-variance-authority@^0.7.1, cn@^0.5.0, lucide-react@^1.61.0, react-aria-components@^1.22.1 ```tsx import { Button } from "@/components/fma-ds/button"; import { DropdownMenu, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuTrigger, } from "@/components/fma-ds/dropdown-menu"; export function DropdownMenuDemo() { return ( My Account Profile ⇧⌘P Billing ⌘B Settings ⌘S ); } ``` ### Emoji Picker An emoji picker with search, skin tones, recently used emoji, custom emoji and a virtualized grid. `` alone renders a trigger and a popover. Compose `EmojiPickerTrigger` + `EmojiPickerContent`, or `EmojiPickerPanel` in place, from `EmojiPickerSearch`, `EmojiPickerCategories`, `EmojiPickerList`, `EmojiPickerPreview` and `EmojiPickerSkinTone`. - **Search**: names, keywords and shortcodes. - **Categories**: a category bar that follows the scroll, plus a frequently or recently used section. - **Data**: emojibase, fetched once per page from jsDelivr (`dataUrl` to self-host, `locale` for 28 languages) or passed as `data`. `emojiVersion="auto"` hides emoji the device can't draw. - **Value**: `value`/`defaultValue`/`onChange` hold the native emoji (or `:shortcode:` for a custom one). `selectionMode="multiple"` toggles a list (with `max`), and `onEmojiSelect` fires on every pick, for inserting into text. - **Keyboard**: focus stays in the search while the arrows move through the grid (a combobox over an `aria-activedescendant` listbox); Enter picks. - **Persistence**: recent picks and the tone persist in localStorage (`recentStorageKey`, `skinToneStorageKey`; `null` keeps them in memory). - **Forms**: `name` submits with a native form. Every string comes from `i18n`. - Install: `pnpm dlx shadcn@latest add @fma-ds/emoji-picker` - Registry item: https://ds.fmartinsandre.dev.br/r/emoji-picker.json - Docs: https://ds.fmartinsandre.dev.br/components/emoji-picker - npm: cn@^0.5.0, lucide-react@^1.61.0, react-aria-components@^1.22.1 - Registry: @fma-ds/button, @fma-ds/dropdown-menu, @fma-ds/empty, @fma-ds/input-group, @fma-ds/popover, @fma-ds/skeleton, @fma-ds/tabs ```tsx import { EmojiPicker } from "@/components/fma-ds/emoji-picker"; export function EmojiPickerDemo() { return ; } ``` ### Empty A placeholder for empty states — no data, no search results, nothing configured yet. Composes an icon/media, title, description, and optional call-to-action content. - Install: `pnpm dlx shadcn@latest add @fma-ds/empty` - Registry item: https://ds.fmartinsandre.dev.br/r/empty.json - Docs: https://ds.fmartinsandre.dev.br/components/empty - npm: class-variance-authority@^0.7.1, cn@^0.5.0 ```tsx import { InboxIcon } from "lucide-react"; import { Button } from "@/components/fma-ds/button"; import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, } from "@/components/fma-ds/empty"; export function EmptyDemo() { return ( No projects yet Get started by creating your first project. ); } ``` ### Field Layout primitives for building form fields. Pairs with `Label`, `Input`, and friends. - **`Field`**: the field wrapper. - **`FieldSet`/`FieldLegend`**: for grouping. - **`FieldLabel`/`FieldDescription`/`FieldError`**: the usual label + hint + error stack. - **`FieldSeparator`**: splits a group. - **`FieldHelp`**: an ⓘ button beside a label or column header that opens help in a popover (touch and keyboard, unlike a tooltip); its required `label` names the subject. - **`choiceCardClassName`**: the choice-card surface shared by `CheckboxCard` and `RadioCard`; hover uses `accent`. With react-aria the control is the card, instead of shadcn's `FieldLabel` wrapping a `Field`, which would nest labels. - Install: `pnpm dlx shadcn@latest add @fma-ds/field` - Registry item: https://ds.fmartinsandre.dev.br/r/field.json - Docs: https://ds.fmartinsandre.dev.br/components/field - npm: class-variance-authority@^0.7.1, cn@^0.5.0, lucide-react@^1.61.0, react-aria-components@^1.22.1 - Registry: @fma-ds/button, @fma-ds/label, @fma-ds/popover, @fma-ds/separator ```tsx import { Field, FieldDescription, FieldLabel } from "@/components/fma-ds/field"; import { Input } from "@/components/fma-ds/input"; export function FieldDemo() { return ( Email We'll never share your email. ); } ``` ### File Type A file type icon: a page with a folded corner and the extension label, in the format's brand or family color (Word blue, Excel green, the GitHub language colors). - **`type`**: one of 171 extensions (documents, certificates, code, config, archives…), one of 13 families (`image`, `folder`… drawn as a glyph) or any other string (a neutral labelled page). - **`getFileType()`**: resolves a file name, path, URL or MIME type, including well-known names without an extension (Dockerfile, `.env.local`, `go.mod`, LICENSE) and aliases such as `.cer`. `getFileTypeCategory()` returns the family. - **`variant`**: `outline`, `solid`, `light` or `mono` (theme gray). A `text-*` class recolors it. - **`size`**: from `mini` (18px, for table rows) to `xl`; a Tailwind `size-*` class scales the label and stroke with the box (container units). - **Content**: `label` replaces the text, `glyph` shows the family glyph and `icon` draws a custom icon. Renders a `span` with `role="img"` named after the type. It has no state, so it works in Server Components. - Install: `pnpm dlx shadcn@latest add @fma-ds/file-type` - Registry item: https://ds.fmartinsandre.dev.br/r/file-type.json - Docs: https://ds.fmartinsandre.dev.br/components/file-type - npm: class-variance-authority@^0.7.1, cn@^0.5.0 ```tsx import { FileType } from "@/components/fma-ds/file-type"; export function FileTypeDemo() { return ; } ``` ### File Upload Picks files by dragging them in, pasting them or browsing, and lists them; it doesn't upload. `FileUpload` holds the `use-file-upload` state — `accept` (`.pdf,.docx`, `image/*`), `maxSize`, `multiple` + `maxFiles`, uncontrolled (`defaultValue`) or controlled (`value`/`onChange`), `isDisabled` — and the parts read it: - **`FileUploadDropzone`**: a react-aria `DropZone`, with keyboard and paste support. By default an icon, title, `description` and a trigger, highlighted with `accent` while a drag is over it. - **`FileUploadTrigger`**: a `FileTrigger` button. - **`FileUploadList`**: renders each file with a function. - **`FileUploadItem`**: a file-type icon or image thumbnail, name, size, optional `progress`, `error`, extra `actions` such as retry, and a remove button that keeps focus in the widget. - **`FileUploadError`**: a `FieldError` with the validation errors (type, size, count). Upload progress and retries stay with the app, keyed by file id. Built-in text is in English; translate labels and error messages with `i18n`. - Install: `pnpm dlx shadcn@latest add @fma-ds/file-upload` - Registry item: https://ds.fmartinsandre.dev.br/r/file-upload.json - Docs: https://ds.fmartinsandre.dev.br/components/file-upload - npm: cn@^0.5.0, lucide-react@^1.61.0, react-aria-components@^1.22.1 - Registry: @fma-ds/button, @fma-ds/field, @fma-ds/file-type, @fma-ds/icon-tile, @fma-ds/progress, @fma-ds/use-file-upload ```tsx import { FileUpload, FileUploadDropzone, FileUploadError, type FileUploadI18n, FileUploadItem, FileUploadList, } from "@/components/fma-ds/file-upload"; const PT_BR: FileUploadI18n = { labels: { dropzone: "Arraste o arquivo para cá", dropzoneMultiple: "Arraste os arquivos para cá", browse: "Selecionar arquivo", browseMultiple: "Selecionar arquivos", remove: (name) => `Remover ${name}`, progress: (name) => `Enviando ${name}`, }, messages: { fileInvalidType: (file) => `"${file.name}" não é um formato aceito.`, fileTooLarge: (file) => `"${file.name}" passa do tamanho máximo.`, tooManyFiles: (maxFiles) => `Envie no máximo ${maxFiles} arquivos.`, }, }; export function FileUploadDemo() { return ( {(file) => } ); } ``` ### Glance A quick piece of information that opens more context one click away — an account notice, a balance, a count of pending items. `Glance` wraps react-aria's `DialogTrigger` and shares a `tone` (`default`, `info`, `success`, `warning`, `destructive`) with its parts. - **`GlanceTrigger`**: the compact button — an icon plus `GlanceText` (a `GlanceLabel` caption over a `GlanceValue`), or icon-only with `size="icon"` and an `aria-label`. - **`GlanceIndicator`**: pins a dot or count to the trigger's corner. - **`GlanceContent`**: a popover holding a dialog (focus moves in, Escape closes, focus returns), laid out with `GlanceHeader`, `GlanceMedia` (an `IconTile` in the tone), `GlanceTitle`, `GlanceDescription` and `GlanceFooter`. Its children can be a function of `{ close }`, to close it from an action. Tone tints the trigger like `Alert`, with no motion — urgency comes from tone and wording. - Install: `pnpm dlx shadcn@latest add @fma-ds/glance` - Registry item: https://ds.fmartinsandre.dev.br/r/glance.json - Docs: https://ds.fmartinsandre.dev.br/components/glance - npm: class-variance-authority@^0.7.1, cn@^0.5.0, react-aria-components@^1.22.1 - Registry: @fma-ds/icon-tile, @fma-ds/popover ```tsx import { CircleDollarSignIcon } from "lucide-react"; import { LinkButton } from "@/components/fma-ds/button"; import { Glance, GlanceContent, GlanceDescription, GlanceFooter, GlanceHeader, GlanceLabel, GlanceMedia, GlanceText, GlanceTitle, GlanceTrigger, GlanceValue, } from "@/components/fma-ds/glance"; export function GlanceDemo() { return ( Saldo de cashback R$ 1.250,00
R$ 1.250,00 disponíveis Acumulados nos últimos 30 dias.
R$ 80,00 expiram nos próximos 5 dias. Ver detalhes
); } ``` ### Hover Card A popover that shows on hover or focus instead of click, for previewing content (a profile card, a link preview) without navigating away. Unlike a tooltip, it can contain interactive content. - Install: `pnpm dlx shadcn@latest add @fma-ds/hover-card` - Registry item: https://ds.fmartinsandre.dev.br/r/hover-card.json - Docs: https://ds.fmartinsandre.dev.br/components/hover-card - npm: cn@^0.5.0, react-aria-components@^1.22.1 ```tsx import { Button } from "@/components/fma-ds/button"; import { HoverCard, HoverCardTrigger } from "@/components/fma-ds/hover-card"; export function HoverCardDemo() { return (

@ada

Mathematician, and the first programmer.

); } ``` ### Icon Stack A layered icon illustration for empty states: three receding cards drawn from `currentColor`, with an optional glyph (`children`) laid flat on the front card. Pairs with `EmptyMedia`. Decorative: the SVG is hidden from assistive tech, so give the glyph `aria-hidden` too. - Install: `pnpm dlx shadcn@latest add @fma-ds/icon-stack` - Registry item: https://ds.fmartinsandre.dev.br/r/icon-stack.json - Docs: https://ds.fmartinsandre.dev.br/components/icon-stack - npm: cn@^0.5.0 ```tsx import { CalendarIcon } from "lucide-react"; import { IconStack } from "@/components/fma-ds/icon-stack"; export function IconStackDemo() { return ( ); } ``` ### Icon Tile A square surface that gives icons a consistent footprint. - **`variant`**: `outline` (bordered), `elevated` (raised chip), `soft` (tinted double container), `solid` (filled) or `frame` (double container matching `Card`). `soft` and `solid` follow `currentColor`, so a text color class retints them. - **`size`**: from `xs` to `xl`. - **`radius`**: `default` (clamped to the tile) or `full` (circular). Renders a ``; wrap it in a `Button` or `Link` to make it interactive. - Install: `pnpm dlx shadcn@latest add @fma-ds/icon-tile` - Registry item: https://ds.fmartinsandre.dev.br/r/icon-tile.json - Docs: https://ds.fmartinsandre.dev.br/components/icon-tile - npm: class-variance-authority@^0.7.1, cn@^0.5.0 ```tsx import { SparklesIcon } from "lucide-react"; import { IconTile } from "@/components/fma-ds/icon-tile"; export function IconTileDemo() { return ( ); } ``` ### Input A single-line text input, styled to match the rest of the registry. Plain native attributes (disabled, not isDisabled) since it wraps react-aria-components' bare Input rather than a higher-level field primitive. - Install: `pnpm dlx shadcn@latest add @fma-ds/input` - Registry item: https://ds.fmartinsandre.dev.br/r/input.json - Docs: https://ds.fmartinsandre.dev.br/components/input - npm: cn@^0.5.0, react-aria-components@^1.22.1 ```tsx import { Input } from "@/components/fma-ds/input"; export function InputDemo() { return ; } ``` ### Input Group Wraps an Input or Textarea with leading/trailing addons (icons, text, or buttons) inside one bordered container, so the whole group shares a single focus ring and border. - Install: `pnpm dlx shadcn@latest add @fma-ds/input-group` - Registry item: https://ds.fmartinsandre.dev.br/r/input-group.json - Docs: https://ds.fmartinsandre.dev.br/components/input-group - npm: class-variance-authority@^0.7.1, cn@^0.5.0, react-aria-components@^1.22.1 - Registry: @fma-ds/button, @fma-ds/input, @fma-ds/textarea ```tsx import { SearchIcon } from "lucide-react"; import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/fma-ds/input-group"; export function InputGroupDemo() { return ( ); } ``` ### Input Otp A one-time-password / verification-code input rendered as individual boxed slots, backed by a single hidden text input for real typing, paste, and autofill behavior. - Install: `pnpm dlx shadcn@latest add @fma-ds/input-otp` - Registry item: https://ds.fmartinsandre.dev.br/r/input-otp.json - Docs: https://ds.fmartinsandre.dev.br/components/input-otp - npm: cn@^0.5.0, input-otp@^1.5.0, lucide-react@^1.61.0 ```tsx import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, } from "@/components/fma-ds/input-otp"; export function InputOtpDemo() { return ( ); } ``` ### Item A generic row layout for lists of things — media/icon, title, description, and trailing actions. - **Rendering**: a `
` or, when given an `href`, a real link (hovered with the theme's `accent`). - **`ItemGroup`**: stacks several with consistent spacing. - Install: `pnpm dlx shadcn@latest add @fma-ds/item` - Registry item: https://ds.fmartinsandre.dev.br/r/item.json - Docs: https://ds.fmartinsandre.dev.br/components/item - npm: class-variance-authority@^0.7.1, cn@^0.5.0, react-aria-components@^1.22.1 - Registry: @fma-ds/separator ```tsx import { UserIcon } from "lucide-react"; import { Button } from "@/components/fma-ds/button"; import { Item, ItemActions, ItemContent, ItemDescription, ItemMedia, ItemTitle, } from "@/components/fma-ds/item"; export function ItemDemo() { return ( Ada Lovelace ada@example.com ); } ``` ### Kbd Displays a keyboard key or shortcut (e.g. ⌘K) inline, styled as a small pill. KbdGroup lays out several keys side by side for combos like ⌘+Shift+P. - Install: `pnpm dlx shadcn@latest add @fma-ds/kbd` - Registry item: https://ds.fmartinsandre.dev.br/r/kbd.json - Docs: https://ds.fmartinsandre.dev.br/components/kbd - npm: cn@^0.5.0, react-aria-components@^1.22.1 ```tsx import { Kbd } from "@/components/fma-ds/kbd"; export function KbdDemo() { return ⌘; } ``` ### Label A form field label. Pair it with a control via `id`/`htmlFor` rather than passing label text as the control's own children: several toggle-style primitives (`Checkbox`, `Switch`, `RadioGroupItem`) render their children inside a small fixed-size box meant for a custom indicator, not prose. - Install: `pnpm dlx shadcn@latest add @fma-ds/label` - Registry item: https://ds.fmartinsandre.dev.br/r/label.json - Docs: https://ds.fmartinsandre.dev.br/components/label - npm: cn@^0.5.0, react-aria-components@^1.22.1 ```tsx import { Label } from "@/components/fma-ds/label"; export function LabelDemo() { return ; } ``` ### Loading Overlay Blocks interaction while an operation runs. No brand animation: just a spinner. - **`LoadingOverlay`**: wraps a region. While `isLoading`, its children stay visible under a veil with a spinner and `label` (default "Loading…", announced via `role="status"`), become `inert`, and the region is `aria-busy`. - **`LoadingScreen`**: the same for the whole app, as a non-dismissable modal (focus held, page hidden from assistive tech, scroll locked) — for operations that must finish before anything else, like saving a status change. Pass a translated `label` as needed. - Install: `pnpm dlx shadcn@latest add @fma-ds/loading-overlay` - Registry item: https://ds.fmartinsandre.dev.br/r/loading-overlay.json - Docs: https://ds.fmartinsandre.dev.br/components/loading-overlay - npm: cn@^0.5.0, lucide-react@^1.61.0, react-aria-components@^1.22.1 ```tsx import { Button } from "@/components/fma-ds/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/fma-ds/card"; import { LoadingOverlay } from "@/components/fma-ds/loading-overlay"; export function LoadingOverlayDemo() { return ( Módulos do cliente Gestor de pedidos e análise de vendas. ); } ``` ### Marker A small inline label used in chat/message timelines — a date separator, a status note, or similar metadata — rendered in muted text with an optional icon. - Install: `pnpm dlx shadcn@latest add @fma-ds/marker` - Registry item: https://ds.fmartinsandre.dev.br/r/marker.json - Docs: https://ds.fmartinsandre.dev.br/components/marker - npm: class-variance-authority@^0.7.1, cn@^0.5.0 ```tsx import { CheckIcon } from "lucide-react"; import { Marker, MarkerContent, MarkerIcon } from "@/components/fma-ds/marker"; export function MarkerDemo() { return ( Deployed 2 minutes ago ); } ``` ### Masked Input An `Input` with live Maskito formatting. Works inside a react-aria `TextField`. - **`mask`**: a preset name (`"cpf"`, `"cnpj"`, `"cpf-cnpj"`, `"cep"`, `"phone"`, `"credit-card"`, `"date"`, `"time"`, `"date-time"`, `"date-range"`, `"decimal"`), a parameterized preset such as `{ type: "decimal", prefix: "R$ " }` or `{ type: "date", mode: "mm/yy" }` for a card expiry (safe to write inline), or custom Maskito options. - **Presets**: set `inputMode`, `autoComplete` and a placeholder. - **Value**: `onValueChange` receives the raw value (digits, ISO date, dot-decimal) and the masked text. `value` may be either, so a form can store only the raw value. For other inputs, such as `InputGroupInput`, spread `useMask(...).inputProps` instead. - Install: `pnpm dlx shadcn@latest add @fma-ds/masked-input` - Registry item: https://ds.fmartinsandre.dev.br/r/masked-input.json - Docs: https://ds.fmartinsandre.dev.br/components/masked-input - Registry: @fma-ds/input, @fma-ds/use-mask ```tsx import { MaskedInput } from "@/components/fma-ds/masked-input"; export function MaskedInputDemo() { return ; } ``` ### Menubar An app-style menu bar (File, Edit, View…). - **Structure**: `Menubar` is a react-aria `Toolbar`, so arrow keys move between menus. Each `MenubarMenu` pairs a `MenubarTrigger` with a `MenubarContent` built on `DropdownMenu`, including submenus (`MenubarSub`). - **Checkable items**: come from react-aria selection — give a `MenubarGroup` a `selectionMode` (`"multiple"` or `"single"`) and `selectedKeys`/`onSelectionChange`, and its items show a check when selected. - **Opening**: menus open on press or Enter/Space/ArrowDown. While one is open, hovering another trigger or pressing ArrowLeft/ArrowRight opens the neighbouring menu. - Install: `pnpm dlx shadcn@latest add @fma-ds/menubar` - Registry item: https://ds.fmartinsandre.dev.br/r/menubar.json - Docs: https://ds.fmartinsandre.dev.br/components/menubar - npm: cn@^0.5.0, react-aria-components@^1.22.1 - Registry: @fma-ds/dropdown-menu ```tsx import * as React from "react"; import type { Selection } from "react-aria-components"; import { Menubar, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, } from "@/components/fma-ds/menubar"; function ViewMenu() { const [panels, setPanels] = React.useState(new Set(["sidebar"])); const [zoom, setZoom] = React.useState(new Set(["100"])); return ( View Sidebar Minimap Zoom 75% 100% 150% ); } export function MenubarDemo() { return ( File New Tab ⌘T New Window Share Email link Messages Print Edit Undo ⌘Z Redo ⇧⌘Z ); } ``` ### Message Layout for one message row in a chat UI — avatar, header, footer, and a content column that aligns to the start or end depending on who sent it. Pairs with Bubble for the actual message content. - Install: `pnpm dlx shadcn@latest add @fma-ds/message` - Registry item: https://ds.fmartinsandre.dev.br/r/message.json - Docs: https://ds.fmartinsandre.dev.br/components/message - npm: cn@^0.5.0 ```tsx import { Bubble, BubbleContent } from "@/components/fma-ds/bubble"; import { Message, MessageAvatar, MessageContent, MessageFooter, MessageHeader, } from "@/components/fma-ds/message"; export function MessageDemo() { return ( AI Assistant Every registry item now has its dependencies verified. 2:14 PM ); } ``` ### Message Scroller An auto-scrolling viewport for chat message lists: sticks to the latest message as new ones arrive, and shows a jump-to-end/start button once the user scrolls away. **Experimental**: it doesn't sit on react-aria-components like the rest of this registry, and `@shadcn/react` is still pre-1.0. - Install: `pnpm dlx shadcn@latest add @fma-ds/message-scroller` - Registry item: https://ds.fmartinsandre.dev.br/r/message-scroller.json - Docs: https://ds.fmartinsandre.dev.br/components/message-scroller - npm: @shadcn/react@^0.3.1, cn@^0.5.0, lucide-react@^1.61.0 - Registry: @fma-ds/button ```tsx import { Message, MessageContent } from "@/components/fma-ds/message"; import { MessageScroller, MessageScrollerButton, MessageScrollerContent, MessageScrollerItem, MessageScrollerProvider, MessageScrollerViewport, } from "@/components/fma-ds/message-scroller"; export function MessageScrollerDemo() { return ( {Array.from({ length: 8 }, (_, i) => ( // biome-ignore lint/suspicious/noArrayIndexKey: fixed-length demo messages, i IS the identity Message {i + 1} ))} ); } ``` ### Native Select A styled wrapper around the real, native (see native-select for that). - Install: `pnpm dlx shadcn@latest add @fma-ds/select` - Registry item: https://ds.fmartinsandre.dev.br/r/select.json - Docs: https://ds.fmartinsandre.dev.br/components/select - npm: cn@^0.5.0, lucide-react@^1.61.0, react-aria-components@^1.22.1 - Registry: @fma-ds/input-group ```tsx import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue, } from "@/components/fma-ds/select"; export function SelectDemo() { return ( ); } ``` ### Separator A thin visual divider between content, horizontal or vertical. - Install: `pnpm dlx shadcn@latest add @fma-ds/separator` - Registry item: https://ds.fmartinsandre.dev.br/r/separator.json - Docs: https://ds.fmartinsandre.dev.br/components/separator - npm: cn@^0.5.0, react-aria-components@^1.22.1 ```tsx import { Separator } from "@/components/fma-ds/separator"; export function SeparatorHorizontal() { return (

Above

Below

); } ``` ### Sheet A modal panel that slides in from an edge of the screen (top/right/bottom/left) — for navigation drawers, filters, or forms that need more room than a popover. - **`size`**: `sm` (384px, default), `md` (448px) or `lg` (672px) caps a left/right sheet from the `sm` breakpoint up; a width in `className` replaces it. - Install: `pnpm dlx shadcn@latest add @fma-ds/sheet` - Registry item: https://ds.fmartinsandre.dev.br/r/sheet.json - Docs: https://ds.fmartinsandre.dev.br/components/sheet - npm: class-variance-authority@^0.7.1, cn@^0.5.0, lucide-react@^1.61.0, react-aria-components@^1.22.1 - Registry: @fma-ds/button ```tsx import { Button } from "@/components/fma-ds/button"; import { Sheet, SheetClose, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, } from "@/components/fma-ds/sheet"; export function SheetDemo() { return ( Edit profile Make changes to your profile here. Save ); } ``` ### Sidebar A collapsible application sidebar with a mobile drawer fallback, a Cmd/Ctrl+B keyboard shortcut, and composable header/content/footer/menu slots. - Install: `pnpm dlx shadcn@latest add @fma-ds/sidebar` - Registry item: https://ds.fmartinsandre.dev.br/r/sidebar.json - Docs: https://ds.fmartinsandre.dev.br/components/sidebar - npm: class-variance-authority@^0.7.1, cn@^0.5.0, lucide-react@^1.61.0, react-aria-components@^1.22.1 - Registry: @fma-ds/button, @fma-ds/input, @fma-ds/separator, @fma-ds/sheet, @fma-ds/skeleton, @fma-ds/tooltip, @fma-ds/use-mobile ```tsx import { HomeIcon, SettingsIcon } from "lucide-react"; import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarTrigger, } from "@/components/fma-ds/sidebar"; export function SidebarDemo() { return ( fma-ds Navigation Home Settings v0.1.0

Page content goes here.

); } ``` ### Skeleton A pulsing placeholder block for content that's still loading — size and shape it with className to match whatever it stands in for. - Install: `pnpm dlx shadcn@latest add @fma-ds/skeleton` - Registry item: https://ds.fmartinsandre.dev.br/r/skeleton.json - Docs: https://ds.fmartinsandre.dev.br/components/skeleton - npm: cn@^0.5.0 ```tsx import { Skeleton } from "@/components/fma-ds/skeleton"; export function SkeletonDemo() { return (
); } ``` ### Slider A draggable single- or range-value slider, horizontal or vertical. Pass an array default/value to get one thumb per value. - **`label`**: names it above the track. - **`showValue`**: prints the value (or range) beside it, formatted by `formatValue` ("200 pedidos") or `formatOptions` (currency, percent — also what screen readers announce). - **`marks`**: labelled stops under the track (`number` or `{ value, label }`). Clicking one moves the nearest thumb there and fires `onChangeEnd`, while the keyboard keeps using the thumb. - **`limit`**: caps the value below `maxValue` (stock left, plan quota). The scale and marks still run to `maxValue`, the track above the limit is drawn as unavailable and its marks are disabled. - **`description`**: under the track, says why, and is read with the value. - Install: `pnpm dlx shadcn@latest add @fma-ds/slider` - Registry item: https://ds.fmartinsandre.dev.br/r/slider.json - Docs: https://ds.fmartinsandre.dev.br/components/slider - npm: cn@^0.5.0, react-aria-components@^1.22.1 - Registry: @fma-ds/label ```tsx import { Slider } from "@/components/fma-ds/slider"; export function SliderDemo() { return (
); } ``` ### Sonner A themed wrapper around the sonner toast library. Renders the toast viewport (Toaster); call toast() from "sonner" anywhere to actually show one. - Install: `pnpm dlx shadcn@latest add @fma-ds/sonner` - Registry item: https://ds.fmartinsandre.dev.br/r/sonner.json - Docs: https://ds.fmartinsandre.dev.br/components/sonner - npm: lucide-react@^1.61.0, next-themes@^0.4.6, sonner@^2.0.8 ```tsx import { toast } from "sonner"; import { Button } from "@/components/fma-ds/button"; import { Toaster } from "@/components/fma-ds/sonner"; export function SonnerDemo() { return (
); } ``` ### Sortable A reorderable list, row or grid (`layout`: `vertical`, `horizontal`, `grid`). Give it the items as `value` (or `defaultValue`) with `onChange`, and one `SortableItem` per item whose `id` matches `getKey(item)` (defaults to `item.id`) — as static children or a render function. - **Pointer**: items drag from anywhere; a line marks the drop position. - **`SortableItemHandle`**: required on every item — the visible grip and its keyboard / screen-reader drag button. Enter starts, arrows pick the drop position, Enter drops; arrows reach it in a vertical list, Tab in a row or grid. - **`isDisabled`**: pins an item in place. - **`selectionMode`**: lets several selected items move together. - **`renderDragPreview`**: replaces the browser's drag snapshot. - **Events**: `onChange` also gets the moved keys and the drop target; `onDragStart`/`onDragEnd` follow react-aria. - Install: `pnpm dlx shadcn@latest add @fma-ds/sortable` - Registry item: https://ds.fmartinsandre.dev.br/r/sortable.json - Docs: https://ds.fmartinsandre.dev.br/components/sortable - npm: cn@^0.5.0, lucide-react@^1.61.0, react-aria-components@^1.22.1 ```tsx import * as React from "react"; import { Sortable, SortableItem, SortableItemHandle } from "@/components/fma-ds/sortable"; interface Task { id: string; title: string; } const TASKS: Task[] = [ { id: "design", title: "Design review" }, { id: "api", title: "API contract" }, { id: "tests", title: "Write tests" }, { id: "ship", title: "Ship it" }, ]; function TaskList({ disabled }: { disabled?: string }) { const [tasks, setTasks] = React.useState(TASKS); return ( {(task) => ( {task.title} )} ); } export function SortableDemo() { return ; } ``` ### Spinner A spinning loading indicator icon (role="status"), sized via className — drop it inside a disabled button or empty state while something loads. - Install: `pnpm dlx shadcn@latest add @fma-ds/spinner` - Registry item: https://ds.fmartinsandre.dev.br/r/spinner.json - Docs: https://ds.fmartinsandre.dev.br/components/spinner - npm: cn@^0.5.0, lucide-react@^1.61.0 ```tsx import { Spinner } from "@/components/fma-ds/spinner"; export function SpinnerDemo() { return ; } ``` ### Stepper Multi-step navigation, horizontal or vertical, built on react-aria `Tabs`. - **Keyboard**: arrow keys, Home and End move between steps; Enter or Space selects one (manual activation). - **Value**: `value`/`defaultValue`/`onValueChange` hold the current step. - **`StepperItem`**: a tab whose `data-state` is `completed` (before the current step, or `isCompleted`), `active` or `inactive`. `isDisabled` skips it and `isLoading` shows the loading indicator. - **`indicators`**: swaps an indicator's content per state. - **`StepperContent`**: panels with the current step's content. - Install: `pnpm dlx shadcn@latest add @fma-ds/stepper` - Registry item: https://ds.fmartinsandre.dev.br/r/stepper.json - Docs: https://ds.fmartinsandre.dev.br/components/stepper - npm: cn@^0.5.0, react-aria-components@^1.22.1 ```tsx import { Stepper, StepperDescription, StepperIndicator, StepperItem, StepperNav, StepperSeparator, StepperTitle, StepperTrigger, } from "@/components/fma-ds/stepper"; const STEPS = [ { step: 1, title: "Account", description: "Create your account" }, { step: 2, title: "Profile", description: "Set up your profile" }, { step: 3, title: "Review", description: "Confirm your details" }, ]; const nav = ( {STEPS.map(({ step, title, description }) => ( {step} {title} {description} {step < STEPS.length && } ))} ); export function StepperDemo() { return ( {nav} ); } ``` ### Switch A two-state on/off toggle switch, in default and compact sizes. - Install: `pnpm dlx shadcn@latest add @fma-ds/switch` - Registry item: https://ds.fmartinsandre.dev.br/r/switch.json - Docs: https://ds.fmartinsandre.dev.br/components/switch - npm: cn@^0.5.0, react-aria-components@^1.22.1 ```tsx import { Label } from "@/components/fma-ds/label"; import { Switch } from "@/components/fma-ds/switch"; export function SwitchDemo() { return (
); } ``` ### Tab Nav Navigation between the pages of an area that looks like tabs: a `