fluentui-webcomponents 0.0.1

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.
Files changed (59) hide show
  1. package/AGENTS.md +212 -0
  2. package/README.md +99 -0
  3. package/components/avatar/fluent-avatar.css +481 -0
  4. package/components/avatar/fluent-avatar.js +80 -0
  5. package/components/badge/fluent-badge.css +289 -0
  6. package/components/badge/fluent-badge.js +20 -0
  7. package/components/breadcrumb/fluent-breadcrumb.css +29 -0
  8. package/components/breadcrumb/fluent-breadcrumb.js +33 -0
  9. package/components/breadcrumb-item/fluent-breadcrumb-item.css +70 -0
  10. package/components/breadcrumb-item/fluent-breadcrumb-item.js +77 -0
  11. package/components/button/fluent-button.css +265 -0
  12. package/components/button/fluent-button.js +326 -0
  13. package/components/card/fluent-card.css +85 -0
  14. package/components/card/fluent-card.js +21 -0
  15. package/components/checkbox/fluent-checkbox.css +171 -0
  16. package/components/checkbox/fluent-checkbox.js +294 -0
  17. package/components/dialog/fluent-dialog.css +82 -0
  18. package/components/dialog/fluent-dialog.js +137 -0
  19. package/components/divider/fluent-divider.css +124 -0
  20. package/components/divider/fluent-divider.js +14 -0
  21. package/components/image/fluent-image.css +73 -0
  22. package/components/image/fluent-image.js +36 -0
  23. package/components/label/fluent-label.css +49 -0
  24. package/components/label/fluent-label.js +61 -0
  25. package/components/link/fluent-link.css +72 -0
  26. package/components/link/fluent-link.js +109 -0
  27. package/components/menu/fluent-menu.css +57 -0
  28. package/components/menu/fluent-menu.js +202 -0
  29. package/components/menu-item/fluent-menu-item.css +152 -0
  30. package/components/menu-item/fluent-menu-item.js +177 -0
  31. package/components/popover/fluent-popover.css +95 -0
  32. package/components/popover/fluent-popover.js +93 -0
  33. package/components/radio/fluent-radio.css +123 -0
  34. package/components/radio/fluent-radio.js +257 -0
  35. package/components/select/fluent-select.css +194 -0
  36. package/components/select/fluent-select.js +245 -0
  37. package/components/slider/fluent-slider.css +199 -0
  38. package/components/slider/fluent-slider.js +438 -0
  39. package/components/spinner/fluent-spinner.css +160 -0
  40. package/components/spinner/fluent-spinner.js +30 -0
  41. package/components/switch/fluent-switch.css +154 -0
  42. package/components/switch/fluent-switch.js +260 -0
  43. package/components/text/fluent-text.css +128 -0
  44. package/components/text/fluent-text.js +21 -0
  45. package/components/text-input/fluent-text-input.css +227 -0
  46. package/components/text-input/fluent-text-input.js +298 -0
  47. package/components/textarea/fluent-textarea.css +227 -0
  48. package/components/textarea/fluent-textarea.js +400 -0
  49. package/components/tooltip/fluent-tooltip.css +65 -0
  50. package/components/tooltip/fluent-tooltip.js +102 -0
  51. package/components/tree/fluent-tree.css +16 -0
  52. package/components/tree/fluent-tree.js +167 -0
  53. package/components/tree-item/fluent-tree-item.css +147 -0
  54. package/components/tree-item/fluent-tree-item.js +163 -0
  55. package/core/fluent-element.js +34 -0
  56. package/gallery.html +492 -0
  57. package/package.json +19 -0
  58. package/theme/theme-picker.js +38 -0
  59. package/tokens.css +724 -0
@@ -0,0 +1,289 @@
1
+ @import url('../../tokens.css');
2
+
3
+ :host { display: inline-flex; }
4
+
5
+ .root {
6
+ position: relative;
7
+ display: inline-flex;
8
+ box-sizing: border-box;
9
+ align-items: center;
10
+ justify-content: center;
11
+ font-family: var(--fontFamilyBase);
12
+ font-weight: var(--fontWeightSemibold);
13
+ font-size: var(--fontSizeBase200);
14
+ line-height: var(--lineHeightBase200);
15
+ min-width: 20px;
16
+ height: 20px;
17
+ padding-inline: calc(var(--spacingHorizontalXS) + var(--spacingHorizontalXXS));
18
+ border-radius: var(--borderRadiusCircular);
19
+ border: 1px solid var(--colorTransparentStroke);
20
+ background-color: var(--colorBrandBackground);
21
+ color: var(--colorNeutralForegroundOnBrand);
22
+ }
23
+
24
+ ::slotted(svg) {
25
+ font-size: 12px;
26
+ }
27
+
28
+ :host(:not([appearance='ghost'])) .root::after {
29
+ position: absolute;
30
+ content: '';
31
+ top: 0;
32
+ left: 0;
33
+ bottom: 0;
34
+ right: 0;
35
+ border-style: solid;
36
+ border-width: var(--strokeWidthThin);
37
+ border-color: inherit;
38
+ border-radius: inherit;
39
+ }
40
+
41
+ /* ===== Shapes ===== */
42
+ :host([shape='square']) .root {
43
+ border-radius: var(--borderRadiusNone);
44
+ }
45
+
46
+ :host([shape='rounded']) .root {
47
+ border-radius: var(--borderRadiusMedium);
48
+ }
49
+
50
+ :host([shape='rounded']:is([size='tiny'], [size='extra-small'], [size='small'])) .root {
51
+ border-radius: var(--borderRadiusSmall);
52
+ }
53
+
54
+ /* ===== Sizes ===== */
55
+ :host([size='tiny']) .root {
56
+ width: 6px;
57
+ height: 6px;
58
+ font-size: 4px;
59
+ line-height: 4px;
60
+ padding-inline: 0;
61
+ min-width: unset;
62
+ }
63
+
64
+ :host([size='tiny']) ::slotted(svg) {
65
+ font-size: 6px;
66
+ }
67
+
68
+ :host([size='extra-small']) .root {
69
+ width: 10px;
70
+ height: 10px;
71
+ font-size: 6px;
72
+ line-height: 6px;
73
+ padding-inline: 0;
74
+ min-width: unset;
75
+ }
76
+
77
+ :host([size='extra-small']) ::slotted(svg) {
78
+ font-size: 10px;
79
+ }
80
+
81
+ :host([size='small']) .root {
82
+ min-width: 16px;
83
+ height: 16px;
84
+ font-size: var(--fontSizeBase100);
85
+ line-height: var(--lineHeightBase100);
86
+ padding-inline: calc(var(--spacingHorizontalXXS) + var(--spacingHorizontalXXS));
87
+ }
88
+
89
+ :host([size='small']) ::slotted(svg) {
90
+ font-size: 12px;
91
+ }
92
+
93
+ :host([size='large']) .root {
94
+ min-width: 24px;
95
+ height: 24px;
96
+ font-size: var(--fontSizeBase200);
97
+ line-height: var(--lineHeightBase200);
98
+ padding-inline: calc(var(--spacingHorizontalXS) + var(--spacingHorizontalXXS));
99
+ }
100
+
101
+ :host([size='large']) ::slotted(svg) {
102
+ font-size: 16px;
103
+ }
104
+
105
+ :host([size='extra-large']) .root {
106
+ min-width: 32px;
107
+ height: 32px;
108
+ font-size: var(--fontSizeBase200);
109
+ line-height: var(--lineHeightBase200);
110
+ padding-inline: calc(var(--spacingHorizontalSNudge) + var(--spacingHorizontalXXS));
111
+ }
112
+
113
+ :host([size='extra-large']) ::slotted(svg) {
114
+ font-size: 20px;
115
+ }
116
+
117
+ /* ===== Filled Appearance (default, no attribute) ===== */
118
+ :host([color='danger']) .root {
119
+ background-color: var(--colorPaletteRedBackground3);
120
+ color: var(--colorNeutralForegroundOnBrand);
121
+ }
122
+
123
+ :host([color='important']) .root {
124
+ background-color: var(--colorNeutralForeground1);
125
+ color: var(--colorNeutralBackground1);
126
+ }
127
+
128
+ :host([color='informative']) .root {
129
+ background-color: var(--colorNeutralBackground5);
130
+ color: var(--colorNeutralForeground3);
131
+ }
132
+
133
+ :host([color='severe']) .root {
134
+ background-color: var(--colorPaletteDarkOrangeBackground3);
135
+ color: var(--colorNeutralForegroundOnBrand);
136
+ }
137
+
138
+ :host([color='subtle']) .root {
139
+ background-color: var(--colorNeutralBackground1);
140
+ color: var(--colorNeutralForeground1);
141
+ }
142
+
143
+ :host([color='success']) .root {
144
+ background-color: var(--colorPaletteGreenBackground3);
145
+ color: var(--colorNeutralForegroundOnBrand);
146
+ }
147
+
148
+ :host([color='warning']) .root {
149
+ background-color: var(--colorPaletteYellowBackground3);
150
+ color: var(--colorNeutralForeground1Static);
151
+ }
152
+
153
+ /* ===== Ghost Appearance ===== */
154
+ :host([appearance='ghost']) .root {
155
+ color: var(--colorBrandForeground1);
156
+ background-color: initial;
157
+ }
158
+
159
+ :host([appearance='ghost'][color='danger']) .root {
160
+ color: var(--colorPaletteRedForeground3);
161
+ }
162
+
163
+ :host([appearance='ghost'][color='important']) .root {
164
+ color: var(--colorNeutralForeground1);
165
+ }
166
+
167
+ :host([appearance='ghost'][color='informative']) .root {
168
+ color: var(--colorNeutralForeground3);
169
+ }
170
+
171
+ :host([appearance='ghost'][color='severe']) .root {
172
+ color: var(--colorPaletteDarkOrangeForeground3);
173
+ }
174
+
175
+ :host([appearance='ghost'][color='subtle']) .root {
176
+ color: var(--colorNeutralForegroundInverted);
177
+ }
178
+
179
+ :host([appearance='ghost'][color='success']) .root {
180
+ color: var(--colorPaletteGreenForeground3);
181
+ }
182
+
183
+ :host([appearance='ghost'][color='warning']) .root {
184
+ color: var(--colorPaletteYellowForeground2);
185
+ }
186
+
187
+ /* ===== Outline Appearance ===== */
188
+ :host([appearance='outline']) .root {
189
+ border-color: currentColor;
190
+ color: var(--colorBrandForeground1);
191
+ background-color: initial;
192
+ }
193
+
194
+ :host([appearance='outline'][color='danger']) .root {
195
+ color: var(--colorPaletteRedForeground3);
196
+ }
197
+
198
+ :host([appearance='outline'][color='important']) .root {
199
+ color: var(--colorNeutralForeground3);
200
+ border-color: var(--colorNeutralStrokeAccessible);
201
+ }
202
+
203
+ :host([appearance='outline'][color='informative']) .root {
204
+ color: var(--colorNeutralForeground3);
205
+ border-color: var(--colorNeutralStroke2);
206
+ }
207
+
208
+ :host([appearance='outline'][color='severe']) .root {
209
+ color: var(--colorPaletteDarkOrangeForeground3);
210
+ }
211
+
212
+ :host([appearance='outline'][color='subtle']) .root {
213
+ color: var(--colorNeutralForegroundStaticInverted);
214
+ }
215
+
216
+ :host([appearance='outline'][color='success']) .root {
217
+ color: var(--colorPaletteGreenForeground2);
218
+ }
219
+
220
+ :host([appearance='outline'][color='warning']) .root {
221
+ color: var(--colorPaletteYellowForeground2);
222
+ }
223
+
224
+ /* ===== Tint Appearance ===== */
225
+ :host([appearance='tint']) .root {
226
+ background-color: var(--colorBrandBackground2);
227
+ color: var(--colorBrandForeground2);
228
+ border-color: var(--colorBrandStroke2);
229
+ }
230
+
231
+ :host([appearance='tint'][color='danger']) .root {
232
+ background-color: var(--colorPaletteRedBackground1);
233
+ color: var(--colorPaletteRedForeground1);
234
+ border-color: var(--colorPaletteRedBorder1);
235
+ }
236
+
237
+ :host([appearance='tint'][color='important']) .root {
238
+ background-color: var(--colorNeutralForeground3);
239
+ color: var(--colorNeutralBackground1);
240
+ border-color: var(--colorTransparentStroke);
241
+ }
242
+
243
+ :host([appearance='tint'][color='informative']) .root {
244
+ background-color: var(--colorNeutralBackground4);
245
+ color: var(--colorNeutralForeground3);
246
+ border-color: var(--colorNeutralStroke2);
247
+ }
248
+
249
+ :host([appearance='tint'][color='severe']) .root {
250
+ background-color: var(--colorPaletteDarkOrangeBackground1);
251
+ color: var(--colorPaletteDarkOrangeForeground1);
252
+ border-color: var(--colorPaletteDarkOrangeBorder1);
253
+ }
254
+
255
+ :host([appearance='tint'][color='subtle']) .root {
256
+ background-color: var(--colorNeutralBackground1);
257
+ color: var(--colorNeutralForeground3);
258
+ border-color: var(--colorNeutralStroke2);
259
+ }
260
+
261
+ :host([appearance='tint'][color='success']) .root {
262
+ background-color: var(--colorPaletteGreenBackground1);
263
+ color: var(--colorPaletteGreenForeground1);
264
+ border-color: var(--colorPaletteGreenBorder2);
265
+ }
266
+
267
+ :host([appearance='tint'][color='warning']) .root {
268
+ background-color: var(--colorPaletteYellowBackground1);
269
+ color: var(--colorPaletteYellowForeground2);
270
+ border-color: var(--colorPaletteYellowBorder1);
271
+ }
272
+
273
+ /* ===== Slot ===== */
274
+ ::slotted([slot='start']) {
275
+ margin-inline-end: var(--spacingHorizontalXXS);
276
+ }
277
+
278
+ ::slotted([slot='end']) {
279
+ margin-inline-start: var(--spacingHorizontalXXS);
280
+ }
281
+
282
+ /* ===== Forced Colors ===== */
283
+ @media (forced-colors: active) {
284
+ .root,
285
+ :host([appearance='outline']) .root,
286
+ :host([appearance='tint']) .root {
287
+ border-color: CanvasText;
288
+ }
289
+ }
@@ -0,0 +1,20 @@
1
+ import { FluentElement } from '../../core/fluent-element.js';
2
+
3
+ const stylesUrl = new URL('./fluent-badge.css', import.meta.url).href;
4
+
5
+ class FluentBadge extends FluentElement {
6
+ static stylesUrl = stylesUrl;
7
+ static template = `
8
+ <div class="root">
9
+ <slot name="start"></slot>
10
+ <slot></slot>
11
+ <slot name="end"></slot>
12
+ </div>
13
+ `;
14
+
15
+ static get observedAttributes() {
16
+ return ['appearance', 'color', 'shape', 'size'];
17
+ }
18
+ }
19
+
20
+ customElements.define('fluent-badge', FluentBadge);
@@ -0,0 +1,29 @@
1
+ @import url('../../tokens.css');
2
+
3
+ :host { display: flex; }
4
+
5
+ .root {
6
+ display: block;
7
+ }
8
+
9
+ .breadcrumb {
10
+ display: flex;
11
+ flex-wrap: wrap;
12
+ align-items: center;
13
+ list-style: none;
14
+ margin: 0;
15
+ padding: 0;
16
+ gap: var(--spacingHorizontalXS);
17
+ font-size: var(--fontSizeBase300);
18
+ line-height: var(--lineHeightBase300);
19
+ }
20
+
21
+ :host([size='large']) .breadcrumb {
22
+ font-size: var(--fontSizeBase400);
23
+ line-height: var(--lineHeightBase400);
24
+ }
25
+
26
+ :host([size='small']) .breadcrumb {
27
+ font-size: var(--fontSizeBase200);
28
+ line-height: var(--lineHeightBase200);
29
+ }
@@ -0,0 +1,33 @@
1
+ import { FluentElement } from '../../core/fluent-element.js';
2
+
3
+ const stylesUrl = new URL('./fluent-breadcrumb.css', import.meta.url).href;
4
+
5
+ class FluentBreadcrumb extends FluentElement {
6
+ static stylesUrl = stylesUrl;
7
+ static template = `
8
+ <div class="root">
9
+ <ol class="breadcrumb" part="breadcrumb">
10
+ <slot></slot>
11
+ </ol>
12
+ </div>
13
+ `;
14
+
15
+ static get observedAttributes() {
16
+ return ['size', 'appearance'];
17
+ }
18
+
19
+ constructor() {
20
+ super();
21
+ this._internals = this.attachInternals();
22
+ }
23
+
24
+ connectedCallback() {
25
+ super.connectedCallback();
26
+ this._internals.role = 'navigation';
27
+ if (!this.hasAttribute('aria-label')) {
28
+ this._internals.ariaLabel = 'Breadcrumb';
29
+ }
30
+ }
31
+ }
32
+
33
+ customElements.define('fluent-breadcrumb', FluentBreadcrumb);
@@ -0,0 +1,70 @@
1
+ @import url("../../tokens.css");
2
+
3
+ :host { display: flex; }
4
+
5
+ .root {
6
+ display: flex;
7
+ align-items: center;
8
+ gap: var(--spacingHorizontalXS);
9
+ }
10
+
11
+ .item {
12
+ display: flex;
13
+ align-items: center;
14
+ gap: var(--spacingHorizontalXS);
15
+ list-style: none;
16
+ }
17
+
18
+ .link {
19
+ color: var(--colorNeutralForeground2);
20
+ text-decoration: none;
21
+ border-radius: var(--borderRadiusMedium);
22
+ padding: var(--spacingVerticalXXS) var(--spacingHorizontalXS);
23
+ transition:
24
+ color var(--durationFaster) var(--curveEasyEaseMax),
25
+ background var(--durationFaster) var(--curveEasyEaseMax);
26
+ }
27
+
28
+ .link:hover {
29
+ color: var(--colorNeutralForeground2Hover);
30
+ background: var(--colorSubtleBackgroundHover);
31
+ }
32
+
33
+ .link:active {
34
+ color: var(--colorNeutralForeground2Pressed);
35
+ background: var(--colorSubtleBackgroundPressed);
36
+ }
37
+
38
+ :host([current]) .link {
39
+ color: var(--colorNeutralForeground1);
40
+ font-weight: var(--fontWeightSemibold);
41
+ pointer-events: none;
42
+ }
43
+
44
+ :host([appearance="subtle"]) .link:hover {
45
+ background: var(--colorSubtleBackgroundHover);
46
+ }
47
+
48
+ .separator {
49
+ display: flex;
50
+ align-items: center;
51
+ color: var(--colorNeutralForeground3);
52
+ flex-shrink: 0;
53
+ }
54
+
55
+ .separator-icon {
56
+ inline-size: 12px;
57
+ block-size: 12px;
58
+ }
59
+
60
+ :host(:first-child) .separator,
61
+ :host([data-position="first"]) .separator {
62
+ display: none;
63
+ }
64
+
65
+ @media (forced-colors: active) {
66
+ .link:hover {
67
+ background: Highlight;
68
+ color: HighlightText;
69
+ }
70
+ }
@@ -0,0 +1,77 @@
1
+ import { FluentElement } from '../../core/fluent-element.js';
2
+
3
+ const stylesUrl = new URL('./fluent-breadcrumb-item.css', import.meta.url).href;
4
+
5
+ class FluentBreadcrumbItem extends FluentElement {
6
+ static stylesUrl = stylesUrl;
7
+ static template = `
8
+ <div class="root">
9
+ <li class="item" part="item">
10
+ <slot name="separator">
11
+ <span class="separator" part="separator" aria-hidden="true">
12
+ <svg class="separator-icon" fill="currentColor" viewBox="0 0 12 12">
13
+ <path d="M4.65 2.15a.5.5 0 000 .7L7.79 6 4.65 9.15a.5.5 0 10.7.7l3.5-3.5a.5.5 0 000-.7l-3.5-3.5a.5.5 0 00-.7 0z"/>
14
+ </svg>
15
+ </span>
16
+ </slot>
17
+ <a class="link" part="link">
18
+ <slot></slot>
19
+ </a>
20
+ </li>
21
+ </div>
22
+ `;
23
+
24
+ static get observedAttributes() {
25
+ return ['href', 'current'];
26
+ }
27
+
28
+ connectedCallback() {
29
+ super.connectedCallback();
30
+ this._linkEl = this._root.querySelector('.link');
31
+ this._syncLink();
32
+ this._updatePosition();
33
+ }
34
+
35
+ disconnectedCallback() {
36
+ super.disconnectedCallback();
37
+ this.removeAttribute('data-position');
38
+ }
39
+
40
+ _updatePosition() {
41
+ const parent = this.parentElement;
42
+ if (!parent) return;
43
+ const siblings = Array.from(parent.children).filter(c => c.tagName === this.tagName);
44
+ const index = siblings.indexOf(this);
45
+ if (index === 0) this.setAttribute('data-position', 'first');
46
+ else if (index === siblings.length - 1) this.setAttribute('data-position', 'last');
47
+ else this.setAttribute('data-position', 'middle');
48
+ }
49
+
50
+ attributeChangedCallback(name, oldVal, newVal) {
51
+ super.attributeChangedCallback(name, oldVal, newVal);
52
+ if (name === 'href' || name === 'current') {
53
+ this._syncLink();
54
+ }
55
+ }
56
+
57
+ _syncLink() {
58
+ if (!this._linkEl) return;
59
+ const href = this.getAttribute('href');
60
+ const isCurrent = this.hasAttribute('current');
61
+
62
+ if (href && !isCurrent) {
63
+ this._linkEl.href = href;
64
+ this._linkEl.removeAttribute('aria-current');
65
+ this._linkEl.removeAttribute('role');
66
+ } else {
67
+ this._linkEl.removeAttribute('href');
68
+ if (isCurrent) {
69
+ this._linkEl.setAttribute('aria-current', 'page');
70
+ } else {
71
+ this._linkEl.removeAttribute('aria-current');
72
+ }
73
+ }
74
+ }
75
+ }
76
+
77
+ customElements.define('fluent-breadcrumb-item', FluentBreadcrumbItem);