Vanilla Custom Elements rewrite of @fluentui/web-components — zero dependencies, zero build tools.
<script type="module" src="core/fluent-element.js"></script>
<script type="module" src="components/button/fluent-button.js"></script>
<script type="module" src="components/badge/fluent-badge.js"></script>
<fluent-button appearance="primary">Click me</fluent-button>
<fluent-badge color="danger">New</fluent-badge>CSS design tokens are defined in tokens.css. Each component's CSS imports it via @import — no <link> needed in your page.
npx serve .Open http://localhost:3000/gallery.html — a single-page showcase of all 26 components with a theme picker.
Accent color (instant, no JS math):
:root { --accent-base-override: #e3008c; }document.documentElement.style.setProperty('--accent-base-override', '#e3008c');The entire 16-stop brand palette derives from this single CSS variable via color-mix(in srgb, ...). All component colors update automatically.
Dark theme (via color-scheme CSS property):
:root { color-scheme: dark; }document.documentElement.style.colorScheme = 'dark';Tokens use light-dark(lightValue, darkValue) — no class toggling needed.
Theme picker (optional):
<script type="module" src="theme/theme-picker.js"></script>
<fluent-theme-picker></fluent-theme-picker>| Component | Tag |
|---|---|
| Button | <fluent-button> |
| Badge | <fluent-badge> |
| Link | <fluent-link> |
| Divider | <fluent-divider> |
| Spinner | <fluent-spinner> |
| Avatar | <fluent-avatar> |
| Checkbox | <fluent-checkbox> |
| Radio | <fluent-radio> |
| Switch | <fluent-switch> |
| Slider | <fluent-slider> |
| TextInput | <fluent-text-input> |
| Textarea | <fluent-textarea> |
| Select | <fluent-select> |
| Card | <fluent-card> |
| Label | <fluent-label> |
| Text | <fluent-text> |
| Image | <fluent-image> |
| Dialog | <fluent-dialog trigger="#btn" close-on="#cancel"> |
| Tooltip | <fluent-tooltip anchor="btn-id"> |
| Menu | <fluent-menu> |
| Breadcrumb | <fluent-breadcrumb> |
| Tree | <fluent-tree> |
fluentui-webcomponents/
├── tokens.css # All design tokens (colors, spacing, type, shadows...)
├── gallery.html # Component showcase
├── standalone-example.html # Minimal usage (no page CSS needed)
├── core/
│ └── fluent-element.js # Base class (~30 lines)
├── components/
│ ├── button/
│ │ ├── fluent-button.js
│ │ └── fluent-button.css # @import url('../../tokens.css');
│ ├── badge/
│ ├── ...
│ └── tree/
└── theme/
└── theme-picker.js # Optional accent/theme switcher
Each component is self-contained: one .js file (class + customElements.define) and one .css file. No bundler, no framework, no external dependencies.
FluentElement— base class extendsHTMLElement, attaches shadow DOM, injects<link rel="stylesheet">for the component's CSS- CSS-only state —
:host([disabled]),:host([appearance="primary"]), etc. Zero JS for visual updates - Form association — native
this.attachInternals()+setFormValue+setValidity - Design tokens —
color-mix(in oklch, var(--accent-base), ...)palette, CSSvar()throughout