https://github.com/graundtech/fluent2-react-kit
React components inspired by Microsoft Fluent 2, built for npm and shadcn-style registry distribution.
https://github.com/graundtech/fluent2-react-kit
Last synced: 15 days ago
JSON representation
React components inspired by Microsoft Fluent 2, built for npm and shadcn-style registry distribution.
- Host: GitHub
- URL: https://github.com/graundtech/fluent2-react-kit
- Owner: graundtech
- License: mit
- Created: 2026-06-27T22:48:05.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-07-11T20:26:30.000Z (29 days ago)
- Last Synced: 2026-07-11T22:10:17.882Z (29 days ago)
- Language: TypeScript
- Homepage: https://fluent2-react-kit-demo.vercel.app
- Size: 167 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: docs/contributing.md
- License: LICENSE
Awesome Lists containing this project
README
# Fluent 2 React Kit
Fluent 2-inspired, shadcn-style React components — copy the source via a registry, or install the package. Built on Tailwind CSS v4 and CSS variables.
> **Disclaimer.** This project is **inspired by Microsoft's Fluent 2 design language**. It is an **independent, community-built open-source project** and is **not affiliated with, sponsored by, or endorsed by Microsoft**. "Fluent" and "Segoe UI" are Microsoft trademarks, referenced here only to describe the visual/behavioral inspiration. **No Fluent UI source code is used** — every token and component in this kit is an original implementation derived from publicly documented Fluent 2 design values (see [`docs/design/tokens-research.md`](docs/design/tokens-research.md) for provenance).
## What this is
`@graundtech/fluent2-react-kit` gives you Fluent 2's visual language with shadcn/ui's developer experience:
- **Fluent 2 aesthetics** — brand blue, flat surfaces, Fluent's radius/elevation/motion scale, Segoe UI-first type stack, all derived from public Fluent 2 design tokens.
- **shadcn/ui philosophy and APIs** — plain function components, `cva` variants, `data-slot` hooks, `asChild` polymorphism, the same prop names (`variant`, `size`, …) shadcn/ui users already know.
- **Registry-first distribution** — every component is a [shadcn registry](https://ui.shadcn.com/docs/registry) item. Install via `npx shadcn add` and get the source copied straight into your project — no runtime dependency required. A traditional npm package is also available.
- **Tailwind v4 + CSS variables** — one token layer (`tokens.css`) drives everything through a Tailwind v4 `@theme inline` bridge. No config-file theming; override CSS custom properties instead.
- **Light, dark, and high-contrast themes** — `.dark` and `.high-contrast` classes, plus automatic `forced-colors` support for Windows high-contrast mode.
- **Tested** — every component ships with Vitest + Testing Library unit tests and an `axe-core` accessibility check (494 tests across the kit today).
Read the full product vision in [`docs/vision.md`](docs/vision.md).
## Quick start
### Using the components (consumers)
**Option A — shadcn registry (recommended).** Point the shadcn CLI at this kit's registry to copy component source directly into your project.
If this is a fresh project, run `init` first (CLI 4.13+ uses presets, not a `--base-color` flag — any preset works, the theme item overrides it):
```bash
npx shadcn@latest init
```
> **Clean up after `init`.** `shadcn init` seeds your global CSS with its own competing theme — delete it before installing `theme`, or it silently wins the cascade over Fluent's tokens. Short version: your global CSS should end up as just `@import "tailwindcss";` followed by the Fluent tokens import, nothing else. Full before/after: [`docs/registry.md`](docs/registry.md#cleaning-up-after-shadcn-init).
Then install the token theme, then any components:
```bash
# 1. Install the token theme first — every component assumes it's present.
npx shadcn@latest add /r/theme.json --yes --overwrite
# 2. Install any component.
npx shadcn@latest add /r/button.json --yes --overwrite
```
> `--yes --overwrite` — `init` pre-creates files like `button.tsx` and `lib/utils.ts` from its preset, so `add` prompts to overwrite them per file; `--overwrite` accepts, and `--yes` is required for that flag to apply non-interactively (scripted installs and CI should always pass both, or the batch hangs/skips files).
> `` is the deployed demo's production domain, `https://fluent2-react-kit.graund.io` — see [`docs/registry.md`](docs/registry.md) for how that constant is wired up and how to point at a local registry (`pnpm demo:dev`) instead.
> [!WARNING]
> **Using Fluent icons in your own components?** Any `@fluentui/react-icons` import must live inside a `"use client"` module. The icons package calls a client-only Griffel styling API at module scope, so importing an icon into a Server Component (e.g. dropping one inside your own `` on a Next.js App Router page) fails `next build`. Kit components that ship with icons already carry `"use client"` — this only bites icons *you* import directly.
**Named registry alias.** Configure a short alias once in `components.json` so you can install with `npx shadcn add @fluent2-react-kit/` instead of the full URL:
```json
// components.json
{
"registries": {
"@fluent2-react-kit": "https://fluent2-react-kit.graund.io/r/{name}.json"
}
}
```
```bash
npx shadcn@latest add @fluent2-react-kit/theme --yes --overwrite
npx shadcn@latest add @fluent2-react-kit/button --yes --overwrite
```
**Option B — npm package.** Install `@graundtech/fluent2-react-kit` and import the token stylesheet:
```bash
npm install @graundtech/fluent2-react-kit
```
```css
/* globals.css */
@import "tailwindcss";
@import "@graundtech/fluent2-react-kit/tokens.css";
@source "../node_modules/@graundtech/fluent2-react-kit/dist";
```
The `@source` line tells Tailwind v4 to scan the package for utility classes (it skips `node_modules` by default — without it the components render unstyled). The path is relative to your CSS file; adjust the `../` depth accordingly.
Full theming details: [`docs/tokens.md`](docs/tokens.md).
### Working on this repo (contributors)
```bash
pnpm install
pnpm build # builds the package, the registry, then the demo
pnpm test # runs the component test suite (Vitest + axe)
pnpm test:e2e # runs the Playwright browser suite (builds + serves the demo)
pnpm demo:dev # runs the demo/showcase app locally
```
The Playwright suite needs a one-time Chromium install (`pnpm exec playwright install chromium`). See [`docs/contributing.md`](docs/contributing.md) for the full contributor workflow, including how to add a new component and how the e2e tests run.
## Component status
All items below are at **v0.6.0**.
| Item | Type | Notes |
| --- | --- | --- |
| `theme` | tokens | Light, dark, and high-contrast CSS variable layer + Tailwind v4 bridge |
| `utils` | lib | The `cn()` class-merging helper (`clsx` + `tailwind-merge`) |
| `accordion` | component | Single/multiple disclosure with animated panel height, built on `@base-ui/react/accordion` |
| `alert` | component | 5 variants (default, destructive, success, warning, info), MessageBar-style icon + title accent |
| `avatar` | component | Image-with-fallback, built on `@base-ui/react/avatar` |
| `badge` | component | 6 variants: default, secondary, destructive, outline, success, warning |
| `breadcrumb` | component | 7-part trail navigation, Server Component-safe (inline SVG glyphs) |
| `button` | component | 6 variants × 6 sizes, brand state ramp, `asChild` |
| `card` | component | 7-part composable card (`Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardAction`, `CardContent`, `CardFooter`) |
| `checkbox` | component | Checked/unchecked/indeterminate, built on `@base-ui/react/checkbox` |
| `combobox` | component | Filterable select — real 10-part family (no Popover+Command recipe), built on `@base-ui/react/combobox` |
| `command` | component | cmdk-style palette without the cmdk dep, built on `@base-ui/react/autocomplete` (inline mode) |
| `dialog` | component | 10-part modal dialog (smoke backdrop, built-in ✕, header/footer), built on `@base-ui/react/dialog` |
| `dropdown-menu` | component | 14-part menu (checkbox/radio items, submenus, shortcuts), built on `@base-ui/react/menu` |
| `input` | component | Text field with the Fluent bottom-accent focus treatment |
| `label` | component | Native ``, optional `required` asterisk |
| `link` | component | 2 variants (default, inline), `asChild` |
| `multi-select` | component | Tags-picker combobox with a chips field, reuses the `combobox` popup parts |
| `overflow` | primitive (headless) | Priority-overflow system (ResizeObserver + exact space accounting) — the single-line Ribbon's core, reusable standalone |
| `pagination` | component | Page navigation styled via `buttonVariants`, Server Component-safe |
| `popover` | component | Anchored non-modal surface (`w-72`, `shadow-16`), built on `@base-ui/react/popover` |
| `progress` | component | Fluent-thin (2px) determinate/indeterminate bar, built on `@base-ui/react/progress` |
| `radio-group` | component | Roving-tabindex radio group (`RadioGroup`, `RadioGroupItem`), built on `@base-ui/react/radio-group` |
| `ribbon` | component | Office-style Ribbon — single-line (priority overflow) **and** classic (group collapse → flyout → scroll) layouts on one tree, `RibbonLayoutSwitcher`; composes Tabs + Toolbar + Overflow |
| `ribbon-collapse` | primitive (headless) | Group-collapse manager (per-group expand/collapse + scroll escalation) — the classic Ribbon's core |
| `select` | component | 9-part composable select (trigger, popup, groups, scroll buttons), built on `@base-ui/react/select` |
| `separator` | component | Horizontal/vertical divider |
| `skeleton` | component | Loading placeholder, `animate-pulse` fill |
| `spinner` | component | Fluent arc-style loading indicator, 4 sizes |
| `split-button` | component | Primary action joined to a chevron menu trigger (Fluent SplitButton), composes `DropdownMenu` |
| `switch` | component | Fluent Toggle Switch look (outlined unchecked track), built on `@base-ui/react/switch` |
| `tabs` | component | Fluent underline TabList with sliding brand indicator, built on `@base-ui/react/tabs` |
| `textarea` | component | Multi-line text field, matches `input`'s focus treatment |
| `toast` | component | Manager-driven notifications, 5 status variants, built on `@base-ui/react/toast` |
| `toggle` | component | Pressed/unpressed toggle button (Fluent ToggleButton), built on `@base-ui/react/toggle` |
| `toolbar` | component | APG toolbar with roving tabindex (`Toolbar`, `ToolbarButton`, `ToolbarGroup`, …), built on `@base-ui/react/toolbar` |
| `tooltip` | component | Neutral elevated surface (Fluent style, not an inverted bubble), built on `@base-ui/react/tooltip` |
That's **33 components + 2 headless primitives (`overflow`, `ribbon-collapse`) + `utils` + `theme`** (37 registry items), all documented in the live demo (`pnpm demo:dev` → `/preview/`). Naming and API conventions are normative in [`docs/component-conventions.md`](docs/component-conventions.md).
For what's planned next — more components, known TODOs, and open backlog — see [`docs/status-and-backlog.md`](docs/status-and-backlog.md).
## Monorepo structure
```
.
├── apps/
│ └── demo/ # Next.js showcase app, deployed to Vercel
│ ├── app/
│ │ ├── page.tsx # Landing page (hero, sample composition, registry install snippet)
│ │ └── preview// # One route per registry item, renders every variant/size
│ └── public/r/ # Generated registry output (gitignored, built by pnpm build:registry)
├── packages/
│ └── react/ # @graundtech/fluent2-react-kit (the published package)
│ └── src/
│ ├── components/ui/ # .tsx + .test.tsx per component
│ ├── lib/utils.ts # cn() helper
│ ├── styles/tokens.css # the Fluent 2 → shadcn token layer
│ ├── test/ # Vitest setup + shared a11y test
│ └── index.ts # package barrel (exports + `version`)
├── registry/
│ └── items/ # One fragment per registry item (.json)
├── scripts/
│ └── build-registry.mjs # Fragments -> apps/demo/public/r/*.json
├── docs/ # This documentation set
├── package.json # Root workspace scripts + packageManager (pnpm)
├── pnpm-workspace.yaml # Declares packages/* and apps/*
└── vercel.json # Release pipeline config (root-directory build)
```
The Vercel release pipeline (Root Directory `apps/demo`, pnpm workspace install, `vercel.json`) depends on this exact layout — restructuring these folders will break the deploy.
## Documentation
| Doc | What it covers |
| --- | --- |
| [`docs/vision.md`](docs/vision.md) | Product vision, target use cases, source-precedence hierarchy, rendering philosophy |
| [`docs/registry.md`](docs/registry.md) | How the registry build works end-to-end, and how to install from it |
| [`docs/tokens.md`](docs/tokens.md) | The design token system: layers, theming, customization |
| [`docs/status-and-backlog.md`](docs/status-and-backlog.md) | Current status and the full component/infra backlog |
| [`docs/contributing.md`](docs/contributing.md) | Contributor setup, scripts, PR expectations |
| [`docs/component-conventions.md`](docs/component-conventions.md) | The normative authoring contract every component follows |
| [`docs/design/tokens-research.md`](docs/design/tokens-research.md) | Token research and provenance (Fluent 2 → shadcn mapping decisions) |
| [`registry/items/README.md`](registry/items/README.md) | Registry fragment schema reference |
## License
[MIT](LICENSE)