@brightspace-ui/core 3.147.2 → 3.147.4

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.
@@ -24,7 +24,7 @@ Breadcrumbs are a way-finding tool that helps users understand where they are w
24
24
  <!-- docs: start donts -->
25
25
  * Don't repeat the current page name in the breadcrumb
26
26
  * Avoid displaying more than one breadcrumb control on a page
27
- * Don't use breadcrumbs as a stepper, see [Wizards](https://github.com/BrightspaceUILabs/wizard) instead
27
+ * Don't use breadcrumbs as a stepper, see [Wizards](https://github.com/BrightspaceUI/labs/tree/main/src/components/wizard) instead
28
28
  <!-- docs: end donts -->
29
29
  <!-- docs: end best practices -->
30
30
 
@@ -15,11 +15,16 @@ A Button is used to communicate and perform an action.
15
15
  <d2l-button-icon text="Icon Button" icon="tier1:gear"></d2l-button-icon>
16
16
  ```
17
17
 
18
- ## Best Practices
18
+ ## Button [d2l-button]
19
+
20
+ Use a Button for most actions, especially if they need to be obvious to the user. For the main action on the page, use the `primary` attribute to draw the user's attention.
21
+
22
+ ### Best Practices
23
+
19
24
  <!-- docs: start best practices -->
20
25
  <!-- docs: start dos -->
21
26
  * Use buttons to cause an action or launch a workflow
22
- * Keep button text short - see "Writing" guidelines
27
+ * Keep `text` short - see "Writing" guidelines
23
28
  <!-- docs: end dos -->
24
29
 
25
30
  <!-- docs: start donts -->
@@ -30,10 +35,6 @@ A Button is used to communicate and perform an action.
30
35
  <!-- docs: end donts -->
31
36
  <!-- docs: end best practices -->
32
37
 
33
- ## Button [d2l-button]
34
-
35
- The `d2l-button` element can be used just like the native button element, but also supports the `primary` attribute for denoting the primary button.
36
-
37
38
  <!-- docs: demo code properties name:d2l-button sandboxTitle:'Button' -->
38
39
  ```html
39
40
  <script type="module">
@@ -55,9 +56,23 @@ The `d2l-button` element can be used just like the native button element, but al
55
56
 
56
57
  ## Subtle Button [d2l-button-subtle]
57
58
 
58
- The `d2l-button-subtle` element can be used just like the native `button`, but for advanced or de-emphasized actions.
59
+ Use subtle buttons for secondary, advanced, or de-emphasized actions that should not compete with primary tasks.
60
+
61
+ ### Best Practices
59
62
 
60
- **Note:** It is strongly recommended to use `text` and `icon` as opposed to putting content in the `slot` to ensure that the recommended subtle button style is maintained.
63
+ <!-- docs: start best practices -->
64
+ <!-- docs: start dos -->
65
+ * Include an icon if you want to draw attention to the action or improve recognition
66
+ * Keep subtle button `text` short - see [Writing guidelines]({{ project.assetPath }}/style-elements/writing/)
67
+ <!-- docs: end dos -->
68
+
69
+ <!-- docs: start donts -->
70
+ * Don’t put content in the slot to set the visible label or icon; use `text` or `icon` to ensure styles are maintained
71
+ * Don’t use subtle buttons for primary or destructive actions — reserve them for less prominent interactions
72
+ * Don't use subtle buttons for navigation, use a [link](../../components/link) instead
73
+ * Don't open menus with subtle buttons - use a [dropdown](../../components/dropdown) instead
74
+ <!-- docs: end donts -->
75
+ <!-- docs: end best practices -->
61
76
 
62
77
  <!-- docs: demo code properties name:d2l-button-subtle sandboxTitle:'Subtle Button' -->
63
78
  ```html
@@ -101,7 +116,23 @@ The `d2l-button-subtle` element can be used just like the native `button`, but f
101
116
 
102
117
  ## Icon Button [d2l-button-icon]
103
118
 
104
- The `d2l-button-icon` element can be used just like the native `button`, for instances where only an icon is displayed.
119
+ Use icon buttons for compact, supplementary actions where space is limited and the action is visually represented by a familiar icon.
120
+
121
+ ### Best Practices
122
+
123
+ <!-- docs: start best practices -->
124
+ <!-- docs: start dos -->
125
+ * Ensure the icon is clear, simple, and universally recognized for its intended action — avoid complex or ambiguous icons
126
+ <!-- docs: end dos -->
127
+
128
+ <!-- docs: start donts -->
129
+ * Don’t use icon-only buttons for critical or primary actions; pair with text if the action is essential or not universally understood
130
+ * Don’t use icons that are unfamiliar, decorative, or easily confused with other actions — stick to established conventions
131
+ * Don’t overload interfaces with too many icon buttons — maintain a clear hierarchy and avoid visual clutter
132
+ * Don't use icon buttons for navigation, use a [link](../../components/link) instead
133
+ * Don't open menus with icon buttons - use a [dropdown](../../components/dropdown) instead
134
+ <!-- docs: end donts -->
135
+ <!-- docs: end best practices -->
105
136
 
106
137
  <!-- docs: demo code properties name:d2l-button-icon sandboxTitle:'Icon Button' -->
107
138
  ```html
@@ -145,8 +176,23 @@ The `d2l-button-icon` element can be used just like the native `button`, for ins
145
176
 
146
177
  ## Toggle Button [d2l-button-toggle]
147
178
 
179
+ Use toggle buttons when users need to easily flip between two opposing states, such as when subscribing or unsubscribing.
180
+
148
181
  The `d2l-button-toggle` element is a container for buttons that toggle a `pressed` state. The component will automatically show or hide the buttons and manage focus based on the `pressed` state. Simply place a `d2l-button-icon` or `d2l-button-subtle` element in each of the `not-pressed` and `pressed` slots. Each button should describe the state and action the user can take.
149
182
 
183
+ ### Best Practices
184
+
185
+ <!-- docs: start best practices -->
186
+ <!-- docs: start dos -->
187
+ * Use when flipping a setting on or off needs to be quick and easy, such as when users are expected to change the setting frequently
188
+ <!-- docs: end dos -->
189
+
190
+ <!-- docs: start donts -->
191
+ * Don’t use toggle buttons for unrelated actions, navigation, or as form inputs — use [checkboxes](../../components/checkbox-input) or [radio buttons](../../components/radio-inputs) for form selection
192
+ * Avoid ambiguous language — users shouldn't have to guess whether the toggle is giving the current state or describing an action it will perform
193
+ <!-- docs: end donts -->
194
+ <!-- docs: end best practices -->
195
+
150
196
  <!-- docs: demo code properties name:d2l-button-toggle sandboxTitle:'Toggle Button' -->
151
197
  ```html
152
198
  <script type="module">
@@ -173,7 +219,27 @@ The `d2l-button-toggle` element is a container for buttons that toggle a `presse
173
219
 
174
220
  ## Split Button [d2l-button-split]
175
221
 
176
- The `d2l-button-split` element is a button component that provides a main button and a slot for `d2l-button-split-item` elements. Simply provide a `key` and `text` for the main button and each item. The `d2l-button-split`'s `click` event provides the `key` of the selected action.
222
+ Use a split button when you need to group a main action with a set of closely related alternative actions, especially when space is limited.
223
+
224
+ The `d2l-button-split` element provides a main button and a slot for `d2l-button-split-item` elements. Simply provide a `key` and `text` for the main button and each item. The `d2l-button-split`'s `click` event provides the `key` of the selected action.
225
+
226
+ ### Best Practices
227
+
228
+ <!-- docs: start best practices -->
229
+ <!-- docs: start dos -->
230
+ * Make the main action the most common action the user takes, while the related actions are similar but less popular or prominent actions
231
+ * For example, a "Save" button with additional options like "Save As" or "Save and Close"
232
+ * Keep split button `text` short - see [Writing guidelines]({{ project.assetPath }}/style-elements/writing/)
233
+ <!-- docs: end dos -->
234
+
235
+ <!-- docs: start donts -->
236
+ * Don’t use split buttons for unrelated or mutually exclusive actions
237
+ * Don’t use split buttons as a replacement for navigation or for actions that should be separate, standalone buttons
238
+ * Avoid including more than 5 actions
239
+ * Don’t use split buttons excessively or in groups; reserve them for cases where the pattern adds real value
240
+ * Don’t use split buttons if there is enough space to show all actions, since the Split Button requires user interaction to reveal hidden options
241
+ <!-- docs: end donts -->
242
+ <!-- docs: end best practices -->
177
243
 
178
244
  <!-- docs: demo code properties name:d2l-button-split sandboxTitle:'Split Button' align:flex-start autoSize:false size:medium -->
179
245
  ```html
@@ -215,7 +281,20 @@ The `d2l-button-split` element is a button component that provides a main button
215
281
 
216
282
  ## Add Button [d2l-button-add]
217
283
 
218
- The `d2l-button-add` is for quickly adding new items at a specific location, such as when adding items to a curated list. Since the Add button is meant to be subtle, it should always be used in combination with more obvious methods to add items (like a menu or primary button).
284
+ Use the Add button when users need to quickly insert new items at specific locations within a curated list or collection.
285
+
286
+ ### Best Practices
287
+
288
+ <!-- docs: start best practices -->
289
+ <!-- docs: start dos -->
290
+ * Place the button where users expect to add items, such as between list elements or at logical insertion points between page sections
291
+ <!-- docs: end dos -->
292
+
293
+ <!-- docs: start donts -->
294
+ * Don’t use the add button as the only method for adding items — always supplement with a more prominent method (such as a main “Add” button or menu option) since the Add button is subtle and may not be discovered by all users
295
+ * Don’t crowd the interface with too many add buttons; use them only where inline addition is helpful and contextually relevant
296
+ <!-- docs: end donts -->
297
+ <!-- docs: end best practices -->
219
298
 
220
299
  <!-- docs: demo code properties name:d2l-button-add sandboxTitle:'Add Button' display:block autoSize:false size:xsmall -->
221
300
  ```html
@@ -238,7 +317,20 @@ The `d2l-button-add` is for quickly adding new items at a specific location, suc
238
317
 
239
318
  Floating workflow buttons `<d2l-floating-buttons>` cause buttons to float or 'dock' to the bottom of the viewport when they would otherwise be below the bottom of the viewport. When their normal position becomes visible, they will undock.
240
319
 
241
- The best time to use floating workflow buttons is when users need immediate access to the buttons without scrolling. An example is a long or complex form page where it's common for users to make frequent isolated edits rather than sequentially completing the form.
320
+ ### Best Practices
321
+
322
+ <!-- docs: start best practices -->
323
+ <!-- docs: start dos -->
324
+ * Use floating workflow buttons to keep important, contextually relevant actions accessible when their normal position would otherwise be out of view
325
+ * An example is a long or complex form page where it's common for users to make frequent isolated edits rather than sequentially completing the form
326
+ * Limit the number of floating buttons to only the most essential actions to reduce clutter and cognitive load.
327
+ * Consider using a [Split Button](../../components/button/#d2l-button-split) if there are several related actions (such as Save, Save As...)
328
+ <!-- docs: end dos -->
329
+
330
+ <!-- docs: start donts -->
331
+ * Don’t use floating buttons for actions that are not critical or frequently needed; avoid floating buttons for secondary or rarely used actions
332
+ <!-- docs: end donts -->
333
+ <!-- docs: end best practices -->
242
334
 
243
335
  <!-- docs: demo code properties name:d2l-floating-buttons sandboxTitle:'Floating Buttons' autoSize:false display:block size:xlarge -->
244
336
  ```html
@@ -1,8 +1,9 @@
1
1
  import '../button/button-icon.js';
2
2
  import '../colors/colors.js';
3
3
  import '../tooltip/tooltip.js';
4
- import { css, html, nothing } from 'lit';
4
+ import { css, html, nothing, unsafeCSS } from 'lit';
5
5
  import { findComposedAncestor, isComposedAncestor } from '../../helpers/dom.js';
6
+ import { getFocusRingStyles, isFocusVisibleSupported, isHasSelectorSupported } from '../../helpers/focus.js';
6
7
  import { heading4Styles, labelStyles } from '../typography/styles.js';
7
8
  import { announce } from '../../helpers/announce.js';
8
9
  import { classMap } from 'lit/directives/class-map.js';
@@ -37,6 +38,10 @@ export function resetHasDisplayedKeyboardTooltip() {
37
38
  hasDisplayedKeyboardTooltip = false;
38
39
  }
39
40
 
41
+ const focusSelector = isHasSelectorSupported() && isFocusVisibleSupported() ?
42
+ '.tag-list-item-container:has(:focus-visible)' :
43
+ ':host(:focus-within) .tag-list-item-container';
44
+
40
45
  export const TagListItemMixin = superclass => class extends LocalizeCoreElement(PropertyRequiredMixin(superclass)) {
41
46
 
42
47
  static get properties() {
@@ -79,10 +84,11 @@ export const TagListItemMixin = superclass => class extends LocalizeCoreElement(
79
84
  display: none;
80
85
  }
81
86
  .tag-list-item-container {
87
+ --d2l-focus-ring-offset: -2px;
82
88
  align-items: center;
83
89
  background-color: var(--d2l-color-regolith);
84
90
  border-radius: 6px;
85
- box-shadow: inset 0 0 0 1px var(--d2l-color-gypsum), 0 2px 4px rgba(0, 0, 0, 0.03);
91
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.03);
86
92
  box-sizing: border-box;
87
93
  color: var(--d2l-color-ferrite);
88
94
  cursor: pointer;
@@ -90,7 +96,8 @@ export const TagListItemMixin = superclass => class extends LocalizeCoreElement(
90
96
  line-height: 1rem;
91
97
  max-width: 320px;
92
98
  min-width: 0;
93
- outline: none;
99
+ outline: 1px solid var(--d2l-color-gypsum);
100
+ outline-offset: -1px;
94
101
  transition: background-color 0.2s ease-out, box-shadow 0.2s ease-out;
95
102
  white-space: nowrap;
96
103
  }
@@ -103,26 +110,13 @@ export const TagListItemMixin = superclass => class extends LocalizeCoreElement(
103
110
  padding: 0.25rem 0.6rem;
104
111
  text-overflow: ellipsis;
105
112
  }
106
- :host(:focus-within) .tag-list-item-container,
107
- :host(:focus-within:hover) .tag-list-item-container {
108
- box-shadow: inset 0 0 0 2px var(--d2l-color-celestine), 0 2px 4px rgba(0, 0, 0, 0.03);
109
- }
110
- @supports selector(:has(a, b)) {
111
- :host(:focus-within) .tag-list-item-container,
112
- :host(:focus-within:hover) .tag-list-item-container {
113
- box-shadow: inset 0 0 0 1px var(--d2l-color-gypsum), 0 2px 4px rgba(0, 0, 0, 0.03);
114
- }
115
- :host(:hover) .tag-list-item-container:has(:focus-visible),
116
- .tag-list-item-container:has(:focus-visible) {
117
- box-shadow: inset 0 0 0 2px var(--d2l-color-celestine), 0 2px 4px rgba(0, 0, 0, 0.03) !important;
118
- }
119
- }
113
+ ${getFocusRingStyles(() => focusSelector)}
120
114
  :host(:hover) .tag-list-item-container,
121
- :host(:focus-within) .tag-list-item-container {
115
+ ${unsafeCSS(focusSelector)} {
122
116
  background-color: var(--d2l-color-sylvite);
123
117
  }
124
- :host(:hover) .tag-list-item-container {
125
- box-shadow: inset 0 0 0 1px var(--d2l-color-mica), 0 2px 4px rgba(0, 0, 0, 0.03);
118
+ :host(:hover) .tag-list-item-container:not(${unsafeCSS(focusSelector)}) {
119
+ outline-color: var(--d2l-color-mica);
126
120
  }
127
121
 
128
122
  @media (prefers-reduced-motion: reduce) {
@@ -153,6 +147,12 @@ export const TagListItemMixin = superclass => class extends LocalizeCoreElement(
153
147
  .d2l-heading-4 {
154
148
  margin: 0 0 0.5rem 0;
155
149
  }
150
+ @media (prefers-contrast: more) {
151
+ :host(:hover) .tag-list-item-container {
152
+ outline-offset: -2px;
153
+ outline-width: 2px;
154
+ }
155
+ }
156
156
  `];
157
157
  }
158
158
 
package/helpers/focus.js CHANGED
@@ -226,3 +226,22 @@ export function isFocusVisibleSupported() {
226
226
 
227
227
  return _isFocusVisibleSupported;
228
228
  }
229
+
230
+ let _isHasSelectorSupported;
231
+
232
+ export function isHasSelectorSupported() {
233
+ if (_isHasSelectorSupported === undefined) {
234
+ const style = document.createElement('style');
235
+ try {
236
+ document.head.appendChild(style);
237
+ style.sheet.insertRule(':has(a) { color: inherit; }');
238
+ _isHasSelectorSupported = true;
239
+ } catch {
240
+ _isHasSelectorSupported = false;
241
+ } finally {
242
+ style.remove();
243
+ }
244
+ }
245
+
246
+ return _isHasSelectorSupported;
247
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "3.147.2",
3
+ "version": "3.147.4",
4
4
  "description": "A collection of accessible, free, open-source web components for building Brightspace applications",
5
5
  "type": "module",
6
6
  "repository": "https://github.com/BrightspaceUI/core.git",