fluentui-webcomponents 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +27 -8
- package/README.md +105 -99
- package/core/fluent-element.js +6 -6
- package/package.json +1 -1
- package/theme/theme-picker.js +24 -5
- package/tokens.css +487 -696
- package/gallery.html +0 -492
package/AGENTS.md
CHANGED
|
@@ -127,21 +127,40 @@ Semantic tokens alias these stops:
|
|
|
127
127
|
--colorBrandBackgroundPressed → --brand-40
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
-
###
|
|
130
|
+
### Theme Switching (light-dark() + color-scheme)
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
Tokens use CSS `light-dark(lightValue, darkValue)` — a single declaration per token. No duplicate dark blocks, no `@media` queries, no `.dark` classes.
|
|
133
|
+
|
|
134
|
+
```css
|
|
135
|
+
:host, :root {
|
|
136
|
+
color-scheme: light dark;
|
|
137
|
+
--accent-base: light-dark(#0f6cbd, #479ef5);
|
|
138
|
+
--neutral-base: light-dark(#808080, #b0b0b0);
|
|
139
|
+
--colorNeutralBackground1: light-dark(white, var(--neutral-shade-24));
|
|
140
|
+
}
|
|
134
141
|
```
|
|
135
142
|
|
|
136
|
-
|
|
143
|
+
Palette stops (`--neutral-10`…`160`, `--brand-10`…`160`, tints, shades) auto-adapt from the `light-dark()` base colors via `color-mix()` — no changes needed.
|
|
137
144
|
|
|
138
|
-
|
|
145
|
+
Page-level overrides use the `color-scheme` CSS property on `:root`:
|
|
139
146
|
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
|
|
147
|
+
```js
|
|
148
|
+
// Light mode
|
|
149
|
+
document.documentElement.style.colorScheme = 'light';
|
|
150
|
+
// Dark mode
|
|
151
|
+
document.documentElement.style.colorScheme = 'dark';
|
|
152
|
+
// Follow system
|
|
153
|
+
document.documentElement.style.colorScheme = 'light dark';
|
|
143
154
|
```
|
|
144
155
|
|
|
156
|
+
### Runtime Accent Change
|
|
157
|
+
|
|
158
|
+
```js
|
|
159
|
+
document.documentElement.style.setProperty('--accent-base', '#e3008c');
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
One line. Overrides the `light-dark()` value. Entire palette and all dependent tokens update instantly. Zero JS math.
|
|
163
|
+
|
|
145
164
|
## Form Association
|
|
146
165
|
|
|
147
166
|
```js
|
package/README.md
CHANGED
|
@@ -1,99 +1,105 @@
|
|
|
1
|
-
# fluentui-webcomponents
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
<script type="module" src="
|
|
10
|
-
<script type="module" src="components/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
<fluent-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
|
54
|
-
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
│
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
1
|
+
# fluentui-webcomponents
|
|
2
|
+
<img src="https://nodei.co/npm/fluentui-webcomponents.svg?style=shields&data=n,v&color=blue"/>
|
|
3
|
+
|
|
4
|
+
Vanilla Custom Elements rewrite of `@fluentui/web-components` — zero dependencies, zero build tools.
|
|
5
|
+
|
|
6
|
+
## Quick Start
|
|
7
|
+
|
|
8
|
+
```html
|
|
9
|
+
<script type="module" src="core/fluent-element.js"></script>
|
|
10
|
+
<script type="module" src="components/button/fluent-button.js"></script>
|
|
11
|
+
<script type="module" src="components/badge/fluent-badge.js"></script>
|
|
12
|
+
|
|
13
|
+
<fluent-button appearance="primary">Click me</fluent-button>
|
|
14
|
+
<fluent-badge color="danger">New</fluent-badge>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
CSS design tokens are defined in `tokens.css`. Each component's CSS imports it via `@import` — no `<link>` needed in your page.
|
|
18
|
+
|
|
19
|
+
## Gallery
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx serve .
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Open `http://localhost:3000/gallery.html` — a single-page showcase of all 26 components with a theme picker.
|
|
26
|
+
|
|
27
|
+
## Customization
|
|
28
|
+
|
|
29
|
+
**Accent color** (instant, no JS math):
|
|
30
|
+
```css
|
|
31
|
+
:root { --accent-base: #e3008c; }
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The entire 16-stop brand palette derives from this single CSS variable via `color-mix(in srgb, ...)`. All component colors update automatically.
|
|
35
|
+
|
|
36
|
+
**Dark theme** (via `color-scheme` CSS property):
|
|
37
|
+
```css
|
|
38
|
+
:root { color-scheme: dark; }
|
|
39
|
+
```
|
|
40
|
+
```js
|
|
41
|
+
document.documentElement.style.colorScheme = 'dark';
|
|
42
|
+
```
|
|
43
|
+
Tokens use `light-dark(lightValue, darkValue)` — no class toggling needed.
|
|
44
|
+
|
|
45
|
+
**Theme picker** (optional):
|
|
46
|
+
```html
|
|
47
|
+
<script type="module" src="theme/theme-picker.js"></script>
|
|
48
|
+
<fluent-theme-picker></fluent-theme-picker>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Components
|
|
52
|
+
|
|
53
|
+
| Component | Tag |
|
|
54
|
+
|---|---|
|
|
55
|
+
| Button | `<fluent-button>` |
|
|
56
|
+
| Badge | `<fluent-badge>` |
|
|
57
|
+
| Link | `<fluent-link>` |
|
|
58
|
+
| Divider | `<fluent-divider>` |
|
|
59
|
+
| Spinner | `<fluent-spinner>` |
|
|
60
|
+
| Avatar | `<fluent-avatar>` |
|
|
61
|
+
| Checkbox | `<fluent-checkbox>` |
|
|
62
|
+
| Radio | `<fluent-radio>` |
|
|
63
|
+
| Switch | `<fluent-switch>` |
|
|
64
|
+
| Slider | `<fluent-slider>` |
|
|
65
|
+
| TextInput | `<fluent-text-input>` |
|
|
66
|
+
| Textarea | `<fluent-textarea>` |
|
|
67
|
+
| Select | `<fluent-select>` |
|
|
68
|
+
| Card | `<fluent-card>` |
|
|
69
|
+
| Label | `<fluent-label>` |
|
|
70
|
+
| Text | `<fluent-text>` |
|
|
71
|
+
| Image | `<fluent-image>` |
|
|
72
|
+
| Dialog | `<fluent-dialog trigger="#btn" close-on="#cancel">` |
|
|
73
|
+
| Tooltip | `<fluent-tooltip anchor="btn-id">` |
|
|
74
|
+
| Menu | `<fluent-menu>` |
|
|
75
|
+
| Breadcrumb | `<fluent-breadcrumb>` |
|
|
76
|
+
| Tree | `<fluent-tree>` |
|
|
77
|
+
|
|
78
|
+
## File Structure
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
fluentui-webcomponents/
|
|
82
|
+
├── tokens.css # All design tokens (colors, spacing, type, shadows...)
|
|
83
|
+
├── gallery.html # Component showcase
|
|
84
|
+
├── standalone-example.html # Minimal usage (no page CSS needed)
|
|
85
|
+
├── core/
|
|
86
|
+
│ └── fluent-element.js # Base class (~30 lines)
|
|
87
|
+
├── components/
|
|
88
|
+
│ ├── button/
|
|
89
|
+
│ │ ├── fluent-button.js
|
|
90
|
+
│ │ └── fluent-button.css # @import url('../../tokens.css');
|
|
91
|
+
│ ├── badge/
|
|
92
|
+
│ ├── ...
|
|
93
|
+
│ └── tree/
|
|
94
|
+
└── theme/
|
|
95
|
+
└── theme-picker.js # Optional accent/theme switcher
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Each component is self-contained: one `.js` file (class + `customElements.define`) and one `.css` file. No bundler, no framework, no external dependencies.
|
|
99
|
+
|
|
100
|
+
## Architecture
|
|
101
|
+
|
|
102
|
+
- **`FluentElement`** — base class extends `HTMLElement`, attaches shadow DOM, injects `<link rel="stylesheet">` for the component's CSS
|
|
103
|
+
- **CSS-only state** — `:host([disabled])`, `:host([appearance="primary"])`, etc. Zero JS for visual updates
|
|
104
|
+
- **Form association** — native `this.attachInternals()` + `setFormValue` + `setValidity`
|
|
105
|
+
- **Design tokens** — `color-mix(in oklch, var(--accent-base), ...)` palette, CSS `var()` throughout
|
package/core/fluent-element.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
class FluentElement extends HTMLElement {
|
|
2
|
-
static stylesUrl =
|
|
3
|
-
static template =
|
|
2
|
+
static stylesUrl = "";
|
|
3
|
+
static template = "";
|
|
4
4
|
|
|
5
5
|
constructor() {
|
|
6
6
|
super();
|
|
7
|
-
this._root = this.attachShadow({ mode:
|
|
7
|
+
this._root = this.attachShadow({ mode: "open" });
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
connectedCallback() {
|
|
11
11
|
if (this.constructor.stylesUrl) {
|
|
12
|
-
const link = document.createElement(
|
|
13
|
-
link.rel =
|
|
12
|
+
const link = document.createElement("link");
|
|
13
|
+
link.rel = "stylesheet";
|
|
14
14
|
link.href = this.constructor.stylesUrl;
|
|
15
15
|
this._root.appendChild(link);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
const tmpl = document.createElement(
|
|
18
|
+
const tmpl = document.createElement("template");
|
|
19
19
|
tmpl.innerHTML = this.constructor.template;
|
|
20
20
|
this._root.appendChild(tmpl.content.cloneNode(true));
|
|
21
21
|
}
|
package/package.json
CHANGED
package/theme/theme-picker.js
CHANGED
|
@@ -18,7 +18,7 @@ class FluentThemePicker extends HTMLElement {
|
|
|
18
18
|
<select id="theme-select" title="Theme">
|
|
19
19
|
<option value="light">Light</option>
|
|
20
20
|
<option value="dark">Dark</option>
|
|
21
|
-
<option value="system">System</option>
|
|
21
|
+
<option value="system" selected>System</option>
|
|
22
22
|
</select>
|
|
23
23
|
Theme
|
|
24
24
|
</label>
|
|
@@ -26,12 +26,31 @@ class FluentThemePicker extends HTMLElement {
|
|
|
26
26
|
this.querySelector('#accent-picker').addEventListener('input', e => {
|
|
27
27
|
document.documentElement.style.setProperty('--accent-base', e.target.value);
|
|
28
28
|
});
|
|
29
|
-
this.querySelector('#theme-select')
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
const themeSelect = this.querySelector('#theme-select');
|
|
30
|
+
|
|
31
|
+
const applyTheme = (theme) => {
|
|
32
|
+
const root = document.documentElement;
|
|
33
|
+
if (theme === 'system') {
|
|
34
|
+
root.style.colorScheme = 'light dark';
|
|
35
|
+
} else {
|
|
36
|
+
root.style.colorScheme = theme;
|
|
33
37
|
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
themeSelect.addEventListener('change', e => {
|
|
41
|
+
applyTheme(e.target.value);
|
|
34
42
|
});
|
|
43
|
+
|
|
44
|
+
if (window.matchMedia) {
|
|
45
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
|
46
|
+
if (themeSelect.value === 'system') {
|
|
47
|
+
applyTheme('system');
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Initialize with the current selection
|
|
53
|
+
applyTheme(themeSelect.value);
|
|
35
54
|
}
|
|
36
55
|
}
|
|
37
56
|
|