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,265 @@
1
+ @import url('../../tokens.css');
2
+
3
+ :host { display: inline-flex; }
4
+
5
+ .root {
6
+ --icon-spacing: var(--spacingHorizontalSNudge);
7
+ position: relative;
8
+ display: inline-flex;
9
+ vertical-align: middle;
10
+ align-items: center;
11
+ box-sizing: border-box;
12
+ justify-content: center;
13
+ text-align: center;
14
+ text-decoration-line: none;
15
+ margin: 0;
16
+ min-height: 32px;
17
+ outline-style: none;
18
+ background-color: var(--colorNeutralBackground1);
19
+ color: var(--colorNeutralForeground1);
20
+ border: var(--strokeWidthThin) solid var(--colorNeutralStroke1);
21
+ padding: 0 var(--spacingHorizontalM);
22
+ min-width: 96px;
23
+ border-radius: var(--borderRadiusMedium);
24
+ font-size: var(--fontSizeBase300);
25
+ font-family: var(--fontFamilyBase);
26
+ font-weight: var(--fontWeightSemibold);
27
+ line-height: var(--lineHeightBase300);
28
+ transition-duration: var(--durationFaster);
29
+ transition-property: background, border, color;
30
+ transition-timing-function: var(--curveEasyEase);
31
+ cursor: pointer;
32
+ user-select: none;
33
+ }
34
+
35
+ .content {
36
+ display: inherit;
37
+ }
38
+
39
+ .root:hover {
40
+ background-color: var(--colorNeutralBackground1Hover);
41
+ color: var(--colorNeutralForeground1Hover);
42
+ border-color: var(--colorNeutralStroke1Hover);
43
+ }
44
+
45
+ .root:hover:active {
46
+ background-color: var(--colorNeutralBackground1Pressed);
47
+ border-color: var(--colorNeutralStroke1Pressed);
48
+ color: var(--colorNeutralForeground1Pressed);
49
+ outline-style: none;
50
+ }
51
+
52
+ .root:focus-visible {
53
+ border-color: var(--colorTransparentStroke);
54
+ outline: var(--strokeWidthThick) solid var(--colorTransparentStroke);
55
+ box-shadow: var(--shadow4), 0 0 0 2px var(--colorStrokeFocus2);
56
+ }
57
+
58
+ ::slotted(svg) {
59
+ font-size: 20px;
60
+ height: 20px;
61
+ width: 20px;
62
+ fill: currentColor;
63
+ }
64
+
65
+ ::slotted([slot='start']) {
66
+ margin-inline-end: var(--icon-spacing);
67
+ }
68
+
69
+ ::slotted([slot='end']),
70
+ [slot='end'] {
71
+ flex-shrink: 0;
72
+ margin-inline-start: var(--icon-spacing);
73
+ }
74
+
75
+ :host([icon-only]) .root {
76
+ min-width: 32px;
77
+ max-width: 32px;
78
+ }
79
+
80
+ :host([size='small']) .root {
81
+ --icon-spacing: var(--spacingHorizontalXS);
82
+ min-height: 24px;
83
+ min-width: 64px;
84
+ padding: 0 var(--spacingHorizontalS);
85
+ border-radius: var(--borderRadiusSmall);
86
+ font-size: var(--fontSizeBase200);
87
+ line-height: var(--lineHeightBase200);
88
+ font-weight: var(--fontWeightRegular);
89
+ }
90
+
91
+ :host([size='small'][icon-only]) .root {
92
+ min-width: 24px;
93
+ max-width: 24px;
94
+ }
95
+
96
+ :host([size='large']) .root {
97
+ min-height: 40px;
98
+ border-radius: var(--borderRadiusLarge);
99
+ padding: 0 var(--spacingHorizontalL);
100
+ font-size: var(--fontSizeBase400);
101
+ line-height: var(--lineHeightBase400);
102
+ }
103
+
104
+ :host([size='large'][icon-only]) .root {
105
+ min-width: 40px;
106
+ max-width: 40px;
107
+ }
108
+
109
+ :host([size='large']) ::slotted(svg) {
110
+ font-size: 24px;
111
+ height: 24px;
112
+ width: 24px;
113
+ }
114
+
115
+ :host(:is([shape='circular'], [shape='circular']:focus-visible)) .root {
116
+ border-radius: var(--borderRadiusCircular);
117
+ }
118
+
119
+ :host(:is([shape='square'], [shape='square']:focus-visible)) .root {
120
+ border-radius: var(--borderRadiusNone);
121
+ }
122
+
123
+ :host([appearance='primary']) .root {
124
+ background-color: var(--colorBrandBackground);
125
+ color: var(--colorNeutralForegroundOnBrand);
126
+ border-color: transparent;
127
+ }
128
+
129
+ :host([appearance='primary']) .root:hover {
130
+ background-color: var(--colorBrandBackgroundHover);
131
+ }
132
+
133
+ :host([appearance='primary']) .root:is(:hover, :hover:active):not(:focus-visible) {
134
+ border-color: transparent;
135
+ }
136
+
137
+ :host([appearance='primary']) .root:is(:hover, :hover:active) {
138
+ color: var(--colorNeutralForegroundOnBrand);
139
+ }
140
+
141
+ :host([appearance='primary']) .root:hover:active {
142
+ background-color: var(--colorBrandBackgroundPressed);
143
+ }
144
+
145
+ :host([appearance='primary']) .root:focus-visible {
146
+ border-color: var(--colorNeutralForegroundOnBrand);
147
+ box-shadow: var(--shadow2), 0 0 0 2px var(--colorStrokeFocus2);
148
+ }
149
+
150
+ :host([appearance='outline']) .root {
151
+ background-color: var(--colorTransparentBackground);
152
+ }
153
+
154
+ :host([appearance='outline']) .root:hover {
155
+ background-color: var(--colorTransparentBackgroundHover);
156
+ }
157
+
158
+ :host([appearance='outline']) .root:hover:active {
159
+ background-color: var(--colorTransparentBackgroundPressed);
160
+ }
161
+
162
+ :host([appearance='subtle']) .root {
163
+ background-color: var(--colorSubtleBackground);
164
+ color: var(--colorNeutralForeground2);
165
+ border-color: transparent;
166
+ }
167
+
168
+ :host([appearance='subtle']) .root:hover {
169
+ background-color: var(--colorSubtleBackgroundHover);
170
+ color: var(--colorNeutralForeground2Hover);
171
+ border-color: transparent;
172
+ }
173
+
174
+ :host([appearance='subtle']) .root:hover:active {
175
+ background-color: var(--colorSubtleBackgroundPressed);
176
+ color: var(--colorNeutralForeground2Pressed);
177
+ border-color: transparent;
178
+ }
179
+
180
+ :host([appearance='subtle']) .root:hover ::slotted(svg) {
181
+ fill: var(--colorNeutralForeground2BrandHover);
182
+ }
183
+
184
+ :host([appearance='subtle']) .root:hover:active ::slotted(svg) {
185
+ fill: var(--colorNeutralForeground2BrandPressed);
186
+ }
187
+
188
+ :host([appearance='transparent']) .root {
189
+ background-color: var(--colorTransparentBackground);
190
+ color: var(--colorNeutralForeground2);
191
+ }
192
+
193
+ :host([appearance='transparent']) .root:hover {
194
+ background-color: var(--colorTransparentBackgroundHover);
195
+ color: var(--colorNeutralForeground2BrandHover);
196
+ }
197
+
198
+ :host([appearance='transparent']) .root:hover:active {
199
+ background-color: var(--colorTransparentBackgroundPressed);
200
+ color: var(--colorNeutralForeground2BrandPressed);
201
+ }
202
+
203
+ :host(:is([appearance='transparent'], [appearance='transparent']:is(:hover, :active))) .root {
204
+ border-color: transparent;
205
+ }
206
+
207
+ :host(:is(:disabled, [disabled-focusable], [appearance]:disabled, [appearance][disabled-focusable])) .root,
208
+ :host(:is(:disabled, [disabled-focusable], [appearance]:disabled, [appearance][disabled-focusable])) .root:hover,
209
+ :host(:is(:disabled, [disabled-focusable], [appearance]:disabled, [appearance][disabled-focusable])) .root:hover:active {
210
+ background-color: var(--colorNeutralBackgroundDisabled);
211
+ border-color: var(--colorNeutralStrokeDisabled);
212
+ color: var(--colorNeutralForegroundDisabled);
213
+ cursor: not-allowed;
214
+ }
215
+
216
+ :host([appearance='primary']:is(:disabled, [disabled-focusable])) .root,
217
+ :host([appearance='primary']:is(:disabled, [disabled-focusable])) .root:is(:hover, :hover:active) {
218
+ border-color: transparent;
219
+ }
220
+
221
+ :host([appearance='outline']:is(:disabled, [disabled-focusable])) .root,
222
+ :host([appearance='outline']:is(:disabled, [disabled-focusable])) .root:is(:hover, :hover:active) {
223
+ background-color: var(--colorTransparentBackground);
224
+ }
225
+
226
+ :host([appearance='subtle']:is(:disabled, [disabled-focusable])) .root,
227
+ :host([appearance='subtle']:is(:disabled, [disabled-focusable])) .root:is(:hover, :hover:active) {
228
+ background-color: var(--colorTransparentBackground);
229
+ border-color: transparent;
230
+ }
231
+
232
+ :host([appearance='transparent']:is(:disabled, [disabled-focusable])) .root,
233
+ :host([appearance='transparent']:is(:disabled, [disabled-focusable])) .root:is(:hover, :hover:active) {
234
+ border-color: transparent;
235
+ background-color: var(--colorTransparentBackground);
236
+ }
237
+
238
+ @media (forced-colors: active) {
239
+ .root {
240
+ background-color: ButtonFace;
241
+ color: ButtonText;
242
+ }
243
+
244
+ .root:is(:hover, :focus-visible) {
245
+ border-color: Highlight !important;
246
+ }
247
+
248
+ :host([appearance='primary']) .root:not(:is(:hover, :focus-visible)) {
249
+ background-color: Highlight;
250
+ color: HighlightText;
251
+ forced-color-adjust: none;
252
+ }
253
+
254
+ :host(:is(:disabled, [disabled-focusable], [appearance]:disabled, [appearance][disabled-focusable])) .root {
255
+ background-color: ButtonFace;
256
+ color: GrayText;
257
+ border-color: ButtonText;
258
+ }
259
+ }
260
+
261
+ @media screen and (prefers-reduced-motion: reduce) {
262
+ .root {
263
+ transition-duration: 0.01ms;
264
+ }
265
+ }
@@ -0,0 +1,326 @@
1
+ import { FluentElement } from '../../core/fluent-element.js';
2
+
3
+ const stylesUrl = new URL('./fluent-button.css', import.meta.url).href;
4
+
5
+ const ButtonType = {
6
+ submit: 'submit',
7
+ reset: 'reset',
8
+ button: 'button',
9
+ };
10
+
11
+ class FluentButton extends FluentElement {
12
+ static formAssociated = true;
13
+ static stylesUrl = stylesUrl;
14
+ static template = `
15
+ <div class="root" part="content">
16
+ <slot name="start"></slot>
17
+ <slot></slot>
18
+ <slot name="end"></slot>
19
+ <slot name="internal"></slot>
20
+ </div>
21
+ `;
22
+
23
+ static get observedAttributes() {
24
+ return ['disabled', 'appearance', 'shape', 'size', 'icon-only', 'disabled-focusable', 'autofocus',
25
+ 'type', 'name', 'value', 'form', 'formaction', 'formenctype', 'formmethod', 'formnovalidate', 'formtarget'];
26
+ }
27
+
28
+ constructor() {
29
+ super();
30
+ this._internals = this.attachInternals();
31
+ this._internals.role = 'button';
32
+ this._type = 'submit';
33
+ this._name = '';
34
+ this._value = '';
35
+ this._disabled = false;
36
+ this._disabledFocusable = false;
37
+ this._autofocus = false;
38
+ this._formAction = '';
39
+ this._formEnctype = '';
40
+ this._formMethod = '';
41
+ this._formNoValidate = false;
42
+ this._formTarget = '';
43
+ this._formId = '';
44
+ this._formSubmissionFallback = null;
45
+
46
+ this._boundKeydown = this._handleKeydown.bind(this);
47
+ this._boundClick = this._handleClick.bind(this);
48
+ }
49
+
50
+ connectedCallback() {
51
+ super.connectedCallback();
52
+ this._root.addEventListener('keydown', this._boundKeydown);
53
+ this._root.addEventListener('click', this._boundClick);
54
+ this._updateTabIndex();
55
+ this._updateAriaDisabled();
56
+ }
57
+
58
+ disconnectedCallback() {
59
+ super.disconnectedCallback();
60
+ this._root.removeEventListener('keydown', this._boundKeydown);
61
+ this._root.removeEventListener('click', this._boundClick);
62
+ }
63
+
64
+ attributeChangedCallback(name, oldVal, newVal) {
65
+ if (oldVal === newVal) return;
66
+
67
+ switch (name) {
68
+ case 'disabled':
69
+ this._disabled = this.hasAttribute('disabled');
70
+ this._updateTabIndex();
71
+ break;
72
+ case 'disabled-focusable':
73
+ this._disabledFocusable = this.hasAttribute('disabled-focusable');
74
+ this._updateAriaDisabled();
75
+ this._updateTabIndex();
76
+ break;
77
+ case 'autofocus':
78
+ this._autofocus = this.hasAttribute('autofocus');
79
+ break;
80
+ case 'type':
81
+ this._type = newVal || 'submit';
82
+ this._removeFallbackControl();
83
+ break;
84
+ case 'name':
85
+ this._name = newVal || '';
86
+ this._updateFallbackControlName();
87
+ break;
88
+ case 'value':
89
+ this._value = newVal || '';
90
+ this._updateFallbackControlValue();
91
+ break;
92
+ case 'form':
93
+ this._formId = newVal || '';
94
+ this._updateFallbackControlForm();
95
+ break;
96
+ case 'formaction':
97
+ this._formAction = newVal || '';
98
+ this._updateFallbackControlFormAction();
99
+ break;
100
+ case 'formenctype':
101
+ this._formEnctype = newVal || '';
102
+ this._updateFallbackControlEnctype();
103
+ break;
104
+ case 'formmethod':
105
+ this._formMethod = newVal || '';
106
+ this._updateFallbackControlMethod();
107
+ break;
108
+ case 'formnovalidate':
109
+ this._formNoValidate = this.hasAttribute('formnovalidate');
110
+ this._updateFallbackControlNoValidate();
111
+ break;
112
+ case 'formtarget':
113
+ this._formTarget = newVal || '';
114
+ this._updateFallbackControlTarget();
115
+ break;
116
+ default:
117
+ super.attributeChangedCallback(name, oldVal, newVal);
118
+ }
119
+ }
120
+
121
+ get type() { return this._type; }
122
+ set type(val) { this.setAttribute('type', val); }
123
+
124
+ get name() { return this._name; }
125
+ set name(val) { this.setAttribute('name', val); }
126
+
127
+ get value() { return this._value; }
128
+ set value(val) { this.setAttribute('value', val); }
129
+
130
+ get form() { return this._internals.form; }
131
+
132
+ get labels() { return Object.freeze(Array.from(this._internals.labels)); }
133
+
134
+ formDisabledCallback(disabled) {
135
+ if (disabled) {
136
+ this.setAttribute('disabled', '');
137
+ } else {
138
+ this.removeAttribute('disabled');
139
+ }
140
+ }
141
+
142
+ _handleKeydown(e) {
143
+ if (e.key === 'Enter' || e.key === ' ') {
144
+ e.preventDefault();
145
+ if (!this._disabled || this._disabledFocusable) {
146
+ this._root.click();
147
+ }
148
+ }
149
+ }
150
+
151
+ _handleClick(e) {
152
+ if (this._disabled && !this._disabledFocusable) {
153
+ e.stopImmediatePropagation();
154
+ return;
155
+ }
156
+
157
+ if (this._type === ButtonType.submit) {
158
+ this._submitForm();
159
+ } else if (this._type === ButtonType.reset) {
160
+ this._resetForm();
161
+ }
162
+ }
163
+
164
+ _submitForm() {
165
+ const form = this._internals.form;
166
+ if (!form || this._disabled) return;
167
+
168
+ const hasOverrides = this._name || this._formAction || this._formEnctype ||
169
+ this._formMethod || this._formNoValidate || this._formTarget || this._formId;
170
+
171
+ if (!hasOverrides) {
172
+ form.requestSubmit();
173
+ return;
174
+ }
175
+
176
+ try {
177
+ this._internals.setFormValue(this._value || '');
178
+ form.requestSubmit(this);
179
+ } catch (err) {
180
+ this._createFallbackControl();
181
+ this._internals.setFormValue(null);
182
+ form.requestSubmit(this._formSubmissionFallback);
183
+ }
184
+ }
185
+
186
+ _resetForm() {
187
+ this._internals.form?.reset();
188
+ }
189
+
190
+ _updateTabIndex() {
191
+ if (this._disabled && !this._disabledFocusable) {
192
+ this.removeAttribute('tabindex');
193
+ } else {
194
+ const authorTabindex = this.getAttribute('tabindex');
195
+ if (authorTabindex !== null && Number(authorTabindex) < 0) {
196
+ this.tabIndex = -1;
197
+ } else {
198
+ this.tabIndex = 0;
199
+ }
200
+ }
201
+ }
202
+
203
+ _updateAriaDisabled() {
204
+ this._internals.ariaDisabled = this._disabledFocusable ? 'true' : 'false';
205
+ }
206
+
207
+ _needsFallbackControl() {
208
+ return !!(this._name || this._formAction || this._formEnctype ||
209
+ this._formMethod || this._formNoValidate || this._formTarget || this._formId);
210
+ }
211
+
212
+ _createFallbackControl() {
213
+ if (this._formSubmissionFallback) return;
214
+
215
+ const fallback = document.createElement('button');
216
+ fallback.style.display = 'none';
217
+ fallback.type = 'submit';
218
+ fallback.setAttribute('slot', 'internal');
219
+
220
+ this._updateFallbackControlAttributes(fallback);
221
+ this._root.appendChild(fallback);
222
+ this._formSubmissionFallback = fallback;
223
+ }
224
+
225
+ _removeFallbackControl() {
226
+ if (this._type !== ButtonType.submit) {
227
+ if (this._formSubmissionFallback) {
228
+ this._formSubmissionFallback.remove();
229
+ this._formSubmissionFallback = null;
230
+ }
231
+ const internalSlot = this._root.querySelector('slot[name="internal"]');
232
+ if (internalSlot) internalSlot.remove();
233
+ }
234
+ }
235
+
236
+ _updateFallbackControlName() {
237
+ if (this._formSubmissionFallback) {
238
+ if (this._name) {
239
+ this._formSubmissionFallback.setAttribute('name', this._name);
240
+ } else {
241
+ this._formSubmissionFallback.removeAttribute('name');
242
+ }
243
+ }
244
+ }
245
+
246
+ _updateFallbackControlValue() {
247
+ if (this._formSubmissionFallback) {
248
+ if (this._value) {
249
+ this._formSubmissionFallback.setAttribute('value', this._value);
250
+ } else {
251
+ this._formSubmissionFallback.removeAttribute('value');
252
+ }
253
+ }
254
+ }
255
+
256
+ _updateFallbackControlForm() {
257
+ if (this._formSubmissionFallback && this._formId) {
258
+ this._formSubmissionFallback.setAttribute('form', this._formId);
259
+ } else if (this._formSubmissionFallback) {
260
+ this._formSubmissionFallback.removeAttribute('form');
261
+ }
262
+ }
263
+
264
+ _updateFallbackControlFormAction() {
265
+ if (this._formSubmissionFallback) {
266
+ if (this._formAction) {
267
+ this._formSubmissionFallback.setAttribute('formaction', this._formAction);
268
+ } else {
269
+ this._formSubmissionFallback.removeAttribute('formaction');
270
+ }
271
+ }
272
+ }
273
+
274
+ _updateFallbackControlEnctype() {
275
+ if (this._formSubmissionFallback) {
276
+ if (this._formEnctype) {
277
+ this._formSubmissionFallback.setAttribute('formenctype', this._formEnctype);
278
+ } else {
279
+ this._formSubmissionFallback.removeAttribute('formenctype');
280
+ }
281
+ }
282
+ }
283
+
284
+ _updateFallbackControlMethod() {
285
+ if (this._formSubmissionFallback) {
286
+ if (this._formMethod) {
287
+ this._formSubmissionFallback.setAttribute('formmethod', this._formMethod);
288
+ } else {
289
+ this._formSubmissionFallback.removeAttribute('formmethod');
290
+ }
291
+ }
292
+ }
293
+
294
+ _updateFallbackControlNoValidate() {
295
+ if (this._formSubmissionFallback) {
296
+ if (this._formNoValidate) {
297
+ this._formSubmissionFallback.setAttribute('formnovalidate', '');
298
+ } else {
299
+ this._formSubmissionFallback.removeAttribute('formnovalidate');
300
+ }
301
+ }
302
+ }
303
+
304
+ _updateFallbackControlTarget() {
305
+ if (this._formSubmissionFallback) {
306
+ if (this._formTarget) {
307
+ this._formSubmissionFallback.setAttribute('formtarget', this._formTarget);
308
+ } else {
309
+ this._formSubmissionFallback.removeAttribute('formtarget');
310
+ }
311
+ }
312
+ }
313
+
314
+ _updateFallbackControlAttributes(fallback) {
315
+ if (this._name) fallback.setAttribute('name', this._name);
316
+ if (this._value) fallback.setAttribute('value', this._value);
317
+ if (this._formId) fallback.setAttribute('form', this._formId);
318
+ if (this._formAction) fallback.setAttribute('formaction', this._formAction);
319
+ if (this._formEnctype) fallback.setAttribute('formenctype', this._formEnctype);
320
+ if (this._formMethod) fallback.setAttribute('formmethod', this._formMethod);
321
+ if (this._formNoValidate) fallback.setAttribute('formnovalidate', '');
322
+ if (this._formTarget) fallback.setAttribute('formtarget', this._formTarget);
323
+ }
324
+ }
325
+
326
+ customElements.define('fluent-button', FluentButton);
@@ -0,0 +1,85 @@
1
+ @import url('../../tokens.css');
2
+
3
+ :host { display: block; }
4
+
5
+ .root {
6
+ display: block;
7
+ box-sizing: border-box;
8
+ background-color: var(--colorNeutralBackground1);
9
+ border: var(--strokeWidthThin) solid var(--colorNeutralStroke2);
10
+ border-radius: var(--borderRadiusXLarge);
11
+ color: var(--colorNeutralForeground1);
12
+ font-family: var(--fontFamilyBase);
13
+ font-size: var(--fontSizeBase300);
14
+ line-height: var(--lineHeightBase300);
15
+ padding: var(--spacingVerticalL) var(--spacingHorizontalL);
16
+ overflow: hidden;
17
+ position: relative;
18
+ }
19
+
20
+ :host([appearance='filled-alternative']) .root {
21
+ background-color: var(--colorNeutralBackground2);
22
+ border-color: transparent;
23
+ }
24
+
25
+ :host([appearance='filled-alternative']) .root:hover {
26
+ background-color: var(--colorNeutralBackground2Hover);
27
+ }
28
+
29
+ :host([appearance='outline']) .root {
30
+ background-color: var(--colorTransparentBackground);
31
+ }
32
+
33
+ :host([appearance='outline']) .root:hover {
34
+ background-color: var(--colorTransparentBackgroundHover);
35
+ }
36
+
37
+ :host([appearance='subtle']) .root {
38
+ background-color: var(--colorSubtleBackground);
39
+ border-color: transparent;
40
+ }
41
+
42
+ :host([appearance='subtle']) .root:hover {
43
+ background-color: var(--colorSubtleBackgroundHover);
44
+ }
45
+
46
+ :host([size='small']) .root {
47
+ padding: var(--spacingVerticalM) var(--spacingHorizontalM);
48
+ border-radius: var(--borderRadiusLarge);
49
+ font-size: var(--fontSizeBase200);
50
+ line-height: var(--lineHeightBase200);
51
+ }
52
+
53
+ :host([size='large']) .root {
54
+ padding: var(--spacingVerticalXL) var(--spacingHorizontalXL);
55
+ font-size: var(--fontSizeBase400);
56
+ line-height: var(--lineHeightBase400);
57
+ }
58
+
59
+ ::slotted([slot='preview']) {
60
+ margin: calc(-1 * var(--spacingVerticalL)) calc(-1 * var(--spacingHorizontalL)) var(--spacingVerticalL);
61
+ width: calc(100% + 2 * var(--spacingHorizontalL));
62
+ display: block;
63
+ }
64
+
65
+ ::slotted([slot='header']) {
66
+ margin-bottom: var(--spacingVerticalM);
67
+ }
68
+
69
+ ::slotted([slot='footer']) {
70
+ margin-top: var(--spacingVerticalM);
71
+ padding-top: var(--spacingVerticalM);
72
+ border-top: var(--strokeWidthThin) solid var(--colorNeutralStroke2);
73
+ }
74
+
75
+ @media (forced-colors: active) {
76
+ .root {
77
+ border-color: ButtonText;
78
+ }
79
+ }
80
+
81
+ @media screen and (prefers-reduced-motion: reduce) {
82
+ .root {
83
+ transition-duration: 0.01ms;
84
+ }
85
+ }
@@ -0,0 +1,21 @@
1
+ import { FluentElement } from '../../core/fluent-element.js';
2
+
3
+ const stylesUrl = new URL('./fluent-card.css', import.meta.url).href;
4
+
5
+ class FluentCard extends FluentElement {
6
+ static stylesUrl = stylesUrl;
7
+ static template = `
8
+ <div class="root">
9
+ <slot name="preview"></slot>
10
+ <slot name="header"></slot>
11
+ <slot></slot>
12
+ <slot name="footer"></slot>
13
+ </div>
14
+ `;
15
+
16
+ static get observedAttributes() {
17
+ return ['appearance', 'size'];
18
+ }
19
+ }
20
+
21
+ customElements.define('fluent-card', FluentCard);