@ulu/frontend 0.2.0-beta.8 → 0.2.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 (53) hide show
  1. package/README.dev.md +36 -13
  2. package/README.md +3 -1
  3. package/dist/es/core/events.js +36 -25
  4. package/dist/es/core/settings.js +33 -22
  5. package/dist/es/index.js +92 -88
  6. package/dist/es/ui/dialog.js +57 -46
  7. package/dist/es/ui/index.d.ts +2 -0
  8. package/dist/es/ui/modal-builder.js +39 -28
  9. package/dist/es/ui/overflow-scroller.js +30 -24
  10. package/dist/es/ui/programmatic-modal.js +55 -0
  11. package/dist/es/ui/proxy-click.js +37 -26
  12. package/dist/es/ui/resizer.js +57 -49
  13. package/dist/es/ui/slider.d.ts.map +1 -1
  14. package/dist/es/ui/slider.js +90 -67
  15. package/dist/es/ui/tab-manager.d.ts +145 -0
  16. package/dist/es/ui/tab-manager.d.ts.map +1 -0
  17. package/dist/es/ui/tab-manager.js +155 -0
  18. package/dist/es/ui/tabs.d.ts +5 -5
  19. package/dist/es/ui/tabs.d.ts.map +1 -1
  20. package/dist/es/ui/tabs.js +34 -51
  21. package/dist/es/ui/theme-toggle.js +80 -69
  22. package/dist/es/ui/tooltip.js +53 -44
  23. package/dist/es/utils/floating-ui.js +35 -24
  24. package/dist/umd/frontend.css +1 -0
  25. package/dist/umd/ulu-frontend.umd.js +40 -47
  26. package/lib/js/exports.md +1 -0
  27. package/lib/js/ui/index.js +8 -0
  28. package/lib/js/ui/slider.js +3 -3
  29. package/lib/js/ui/tab-manager.js +324 -0
  30. package/lib/js/ui/tabs.js +33 -92
  31. package/lib/scss/_breakpoint.scss +3 -3
  32. package/lib/scss/_button.scss +3 -3
  33. package/lib/scss/_color.scss +3 -2
  34. package/lib/scss/_element.scss +10 -4
  35. package/lib/scss/_layout.scss +11 -4
  36. package/lib/scss/_selector.scss +2 -1
  37. package/lib/scss/_typography.scss +9 -10
  38. package/lib/scss/_utils.scss +52 -19
  39. package/lib/scss/base/_elements.scss +1 -1
  40. package/lib/scss/components/_basic-hero.scss +1 -1
  41. package/lib/scss/components/_button-verbose.scss +2 -2
  42. package/lib/scss/components/_callout.scss +3 -4
  43. package/lib/scss/components/_css-icon.scss +2 -2
  44. package/lib/scss/components/_data-grid.scss +5 -5
  45. package/lib/scss/components/_flipcard.scss +3 -2
  46. package/lib/scss/components/_index.scss +6 -0
  47. package/lib/scss/components/_panel.scss +1 -1
  48. package/lib/scss/components/_popover.scss +9 -6
  49. package/lib/scss/components/_tabs.scss +20 -5
  50. package/lib/scss/components/_tagged.scss +59 -0
  51. package/package.json +25 -35
  52. package/dist/umd/style.css +0 -1
  53. package/lib/js/ui/dialog.todo +0 -3
package/lib/js/exports.md CHANGED
@@ -92,6 +92,7 @@ These exports originate from the `lib/js/ui/` directory.
92
92
  - `sliderInit`
93
93
  - `sliderInitializer`
94
94
  - `sliderSetupSlider`
95
+ - `TabManager`
95
96
  - `tabsInit`
96
97
  - `tabsInitializer`
97
98
  - `tabsInstances`
@@ -78,6 +78,10 @@ export {
78
78
  setDefaults as modalBuilderSetDefaults,
79
79
  } from "./modal-builder.js";
80
80
 
81
+ export {
82
+ ProgrammaticModalManager
83
+ } from "./programmatic-modal.js";
84
+
81
85
  export {
82
86
  OverflowScroller
83
87
  } from "./overflow-scroller.js";
@@ -139,6 +143,10 @@ export {
139
143
  Slider,
140
144
  } from "./slider.js";
141
145
 
146
+ export {
147
+ TabManager,
148
+ } from "./tab-manager.js";
149
+
142
150
  export {
143
151
  init as tabsInit,
144
152
  initializer as tabsInitializer,
@@ -32,7 +32,6 @@
32
32
 
33
33
  import { ComponentInitializer } from "../core/component.js";
34
34
  import { wrapSettingString } from "../core/settings.js";
35
- import maintain from 'ally.js/maintain/_maintain';
36
35
  import { hasRequiredProps } from '@ulu/utils/object.js';
37
36
  import { trimWhitespace } from "@ulu/utils/string.js";
38
37
  import { debounce } from "@ulu/utils/performance.js";
@@ -392,8 +391,9 @@ export class Slider {
392
391
  }
393
392
 
394
393
  // Make all slide interactive elements inert
395
- const lockInteractives = maintain.disabled({ context: this.elements.track });
394
+ this.elements.track.inert = true;
396
395
  this.transitioning = true;
396
+
397
397
  // Set classes first just feels better
398
398
  if (old) old.navButton.classList.remove(activeClass);
399
399
  slide.navButton.classList.add(activeClass);
@@ -403,8 +403,8 @@ export class Slider {
403
403
  this.index = index;
404
404
  this.slide = slide;
405
405
  this.transitioning = false;
406
+ this.elements.track.inert = false;
406
407
  elements.container.classList.remove(transitionClass);
407
- lockInteractives.disengage();
408
408
  if (!isInit) {
409
409
  slide.element.focus();
410
410
  this.emit("goto", [event, index, slide]);
@@ -0,0 +1,324 @@
1
+ /**
2
+ * @module ui/tab-manager
3
+ */
4
+
5
+ import { ensureId } from "../utils/id.js";
6
+ import { getCoreEventName } from "../core/events.js";
7
+
8
+ /**
9
+ * @typedef {Object} TabManagerOptions
10
+ * @property {String|null} [orientation=null] - "horizontal"|"vertical", auto-detected if omitted.
11
+ * @property {Number} [initialIndex=0] - Index to activate on load.
12
+ * @property {Boolean} [allArrows=false] - Allow all arrow keys to navigate regardless of orientation.
13
+ * @property {Boolean} [openByUrlHash=false] - Activate tab based on URL hash on initialization.
14
+ * @property {Boolean} [setUrlHash=false] - Update URL hash when a new tab is activated.
15
+ * @property {Boolean} [equalHeights=false] - Automatically match the height of all panels.
16
+ * @property {Function|null} [onReady=null] - Callback fired after initialization: (instance) => {}
17
+ * @property {Function|null} [onChange=null] - Callback fired when tab changes: (active, previous) => {}
18
+ */
19
+
20
+ /**
21
+ * Class for managing Aria tabs
22
+ * - Designed to be minimal and lightweight but cover all traditional needs
23
+ * - Designed for static / traditional webpages (not SPA)
24
+ * - Separated from tabs.js so it can be used by itself as needed (tree-shaking)
25
+ */
26
+ export class TabManager {
27
+ /**
28
+ * Default options for TabManager.
29
+ * @type {TabManagerOptions}
30
+ */
31
+ static defaults = {
32
+ orientation: null,
33
+ initialIndex: 0,
34
+ allArrows: false,
35
+ openByUrlHash: false,
36
+ setUrlHash: false,
37
+ equalHeights: false,
38
+ onReady: null,
39
+ onChange: null
40
+ };
41
+
42
+ /**
43
+ * @param {HTMLElement} tablistElement - The element with role="tablist"
44
+ * @param {Partial<TabManagerOptions>} [options] - Configuration options.
45
+ */
46
+ constructor(tablistElement, options = {}) {
47
+ this.tablist = tablistElement;
48
+ this.options = { ...TabManager.defaults, ...options };
49
+ this.tabs = Array.from(this.tablist.children);
50
+
51
+ // Discover panels via `aria-controls`
52
+ this.panels = this.tabs.map(tab => {
53
+ const controlsId = tab.getAttribute('aria-controls');
54
+ return controlsId ? document.getElementById(controlsId) : null;
55
+ }).filter(Boolean); // Ensure no nulls in panels array
56
+
57
+ this.currentIndex = -1;
58
+
59
+ // Bind methods
60
+ this.handleKeydown = this.handleKeydown.bind(this);
61
+ this.handleClick = this.handleClick.bind(this);
62
+
63
+ // Bind the new height update method for the event listener
64
+ this.updatePanelHeights = this.updatePanelHeights.bind(this);
65
+
66
+ if (this.tabs.length === 0 || this.tabs.length !== this.panels.length) {
67
+ console.warn("TabManager: Tab/Panel count mismatch. Check aria-controls.", { tabs: this.tabs, panels: this.panels });
68
+ return;
69
+ }
70
+
71
+ this.orientation = this.options.orientation || this.tablist.getAttribute("aria-orientation") || "horizontal";
72
+
73
+ this.setupAttributes();
74
+ this.attachListeners();
75
+
76
+ // Handle initial state from URL hash if configured
77
+ let startingIndex = this.options.initialIndex;
78
+ if (this.options.openByUrlHash) {
79
+ const hash = window.location.hash.substring(1);
80
+ const hashIndex = this.tabs.findIndex(tab => tab.id === hash);
81
+ if (hashIndex > -1) {
82
+ startingIndex = hashIndex;
83
+ }
84
+ }
85
+
86
+ this.activate(startingIndex, false);
87
+
88
+ // Handle equal heights on init and on resize
89
+ if (this.options.equalHeights) {
90
+ this.updatePanelHeights();
91
+ document.addEventListener(getCoreEventName('pageResized'), this.updatePanelHeights);
92
+ }
93
+
94
+ if (this.options.onReady) {
95
+ this.options.onReady(this);
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Sets the necessary ARIA attributes and initial states for tabs and panels.
101
+ * @private
102
+ */
103
+ setupAttributes() {
104
+ this.tablist.setAttribute("role", "tablist");
105
+
106
+ this.tabs.forEach((tab, index) => {
107
+ const panel = this.panels[index];
108
+
109
+ // Ensure elements have IDs for ARIA attributes
110
+ ensureId(tab);
111
+ ensureId(panel);
112
+
113
+ // Set ARIA Roles & Relationships
114
+ tab.setAttribute("role", "tab");
115
+ // This is now the primary link, but we still ensure it's set.
116
+ if (!tab.hasAttribute('aria-controls')) {
117
+ tab.setAttribute("aria-controls", panel.id);
118
+ }
119
+
120
+ panel.setAttribute("role", "tabpanel");
121
+ panel.setAttribute("aria-labelledby", tab.id);
122
+
123
+ // Initial hidden state
124
+ panel.hidden = true;
125
+ tab.setAttribute("tabindex", "-1");
126
+ tab.setAttribute("aria-selected", "false");
127
+ });
128
+ }
129
+
130
+ /**
131
+ * Attaches click and keydown event listeners to each tab.
132
+ * @private
133
+ */
134
+ attachListeners() {
135
+ this.tabs.forEach(tab => {
136
+ tab.addEventListener("click", this.handleClick);
137
+ tab.addEventListener("keydown", this.handleKeydown);
138
+ });
139
+ }
140
+
141
+ /**
142
+ * Handles click events on tabs, activating the corresponding panel.
143
+ * @param {MouseEvent} e - The click event.
144
+ * @private
145
+ */
146
+ handleClick(e) {
147
+ const index = this.tabs.indexOf(e.currentTarget);
148
+ this.activate(index);
149
+ }
150
+
151
+ /**
152
+ * Handles keyboard navigation (arrows, Home, End) on the tab list.
153
+ * @param {KeyboardEvent} e - The keydown event.
154
+ * @private
155
+ */
156
+ handleKeydown(e) {
157
+ const index = this.tabs.indexOf(e.currentTarget);
158
+ let nextIndex = null;
159
+ const isVert = this.orientation === "vertical";
160
+ const allArrows = this.options.allArrows;
161
+ const isRtl = (this.tablist.dir === 'rtl' || document.dir === 'rtl') && this.tablist.dir !== 'ltr';
162
+
163
+ const keyNext = isRtl ? 'ArrowLeft' : 'ArrowRight';
164
+ const keyPrev = isRtl ? 'ArrowRight' : 'ArrowLeft';
165
+
166
+ // Vertical movement
167
+ if (e.key === "ArrowDown") {
168
+ if (isVert || allArrows) nextIndex = (index + 1) % this.tabs.length;
169
+ } else if (e.key === "ArrowUp") {
170
+ if (isVert || allArrows) nextIndex = (index - 1 + this.tabs.length) % this.tabs.length;
171
+ // Horizontal movement
172
+ } else if (e.key === keyNext) {
173
+ if (!isVert || allArrows) nextIndex = (index + 1) % this.tabs.length;
174
+ } else if (e.key === keyPrev) {
175
+ if (!isVert || allArrows) nextIndex = (index - 1 + this.tabs.length) % this.tabs.length;
176
+ // Other keys
177
+ } else if (e.key === "Home") {
178
+ nextIndex = 0;
179
+ } else if (e.key === "End") {
180
+ nextIndex = this.tabs.length - 1;
181
+ }
182
+
183
+ if (nextIndex !== null) {
184
+ e.preventDefault();
185
+ this.activate(nextIndex);
186
+ this.tabs[nextIndex].focus();
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Activates a tab. Can be called with an index or a tab ID string.
192
+ * @param {Number|String} indexOrId - The index or ID of the tab to activate.
193
+ * @param {Boolean} [triggerActions=true] - If false, will not fire onChange or set URL hash.
194
+ */
195
+ activate(indexOrId, triggerActions = true) {
196
+ let index = -1;
197
+ if (typeof indexOrId === "string") {
198
+ index = this.tabs.findIndex(tab => tab.id === indexOrId);
199
+ } else {
200
+ index = indexOrId;
201
+ }
202
+
203
+ if (index < 0 || index >= this.tabs.length) return;
204
+ if (this.currentIndex === index) return;
205
+
206
+ const prevIndex = this.currentIndex;
207
+ const prevTab = prevIndex > -1 ? this.tabs[prevIndex] : null;
208
+ const prevPanel = prevIndex > -1 ? this.panels[prevIndex] : null;
209
+
210
+ // Deactivate Current
211
+ if (prevTab) {
212
+ prevTab.setAttribute("aria-selected", "false");
213
+ prevTab.setAttribute("tabindex", "-1");
214
+ prevPanel.hidden = true;
215
+ }
216
+
217
+ // Activate New
218
+ const tab = this.tabs[index];
219
+ const panel = this.panels[index];
220
+
221
+ tab.setAttribute("aria-selected", "true");
222
+ tab.setAttribute("tabindex", "0");
223
+ panel.hidden = false;
224
+
225
+ this.currentIndex = index;
226
+
227
+ // Update URL hash if configured and not the initial silent activation
228
+ if (triggerActions && this.options.setUrlHash && window.history) {
229
+ window.history.replaceState(null, "", `#${ tab.id }`);
230
+ }
231
+
232
+ // Fire onChange callback
233
+ if (triggerActions && this.options.onChange) {
234
+ this.options.onChange(
235
+ { index, tab, panel },
236
+ { index: prevIndex, tab: prevTab, panel: prevPanel }
237
+ );
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Public method to activate a tab by its ID.
243
+ * @param {String} id - The ID of the tab element to activate.
244
+ */
245
+ activateById(id) {
246
+ this.activate(id, true);
247
+ }
248
+
249
+ /**
250
+ * Calculates and applies equal heights to all panels.
251
+ * Waits for images within panels to load before calculating.
252
+ */
253
+ updatePanelHeights() {
254
+ if (!this.panels || this.panels.length === 0) return;
255
+
256
+ const parent = this.panels[0].parentElement;
257
+ if (!parent) return;
258
+ const images = [ ...parent.querySelectorAll("img") ];
259
+
260
+ const imagePromise = (image) => new Promise((resolve) => {
261
+ if (image.complete) return resolve(image);
262
+ image.onload = () => resolve(image);
263
+ image.onerror = () => resolve(image); // Resolve on error so it doesn't block
264
+ });
265
+
266
+ const imagePromises = images.map(imagePromise);
267
+
268
+ Promise.all(imagePromises).then(() => {
269
+ // Reset heights to auto before measuring to get natural height
270
+ this.panels.forEach(panel => {
271
+ panel.style.minHeight = '';
272
+ });
273
+
274
+ const heights = this.panels.map(panel => {
275
+ const wasHidden = panel.hidden;
276
+ panel.hidden = false;
277
+ const panelHeight = panel.offsetHeight;
278
+ panel.hidden = wasHidden;
279
+ return panelHeight;
280
+ });
281
+
282
+ const max = Math.max(...heights);
283
+ if (max > 0) {
284
+ this.panels.forEach(panel => {
285
+ panel.style.minHeight = `${ max }px`;
286
+ });
287
+ }
288
+ });
289
+ }
290
+
291
+ /**
292
+ * Removes event listeners, cleans up ARIA attributes, and resets the DOM to its pre-initialized state.
293
+ */
294
+ destroy() {
295
+ this.tabs.forEach(tab => {
296
+ tab.removeEventListener("click", this.handleClick);
297
+ tab.removeEventListener("keydown", this.handleKeydown);
298
+ });
299
+ if (this.options.equalHeights) {
300
+ document.removeEventListener(getCoreEventName('pageResized'), this.updatePanelHeights);
301
+ }
302
+
303
+ this.tablist.removeAttribute("role");
304
+
305
+ this.tabs.forEach(tab => {
306
+ tab.removeAttribute("role");
307
+ tab.removeAttribute("aria-selected");
308
+ tab.removeAttribute("tabindex");
309
+ });
310
+
311
+ this.panels.forEach(panel => {
312
+ panel.removeAttribute("role");
313
+ panel.removeAttribute("aria-labelledby");
314
+ panel.hidden = false;
315
+ panel.style.minHeight = '';
316
+ });
317
+
318
+ this.tablist = null;
319
+ this.tabs = [];
320
+ this.panels = [];
321
+ this.options = {};
322
+ this.currentIndex = -1;
323
+ }
324
+ }
package/lib/js/ui/tabs.js CHANGED
@@ -2,14 +2,9 @@
2
2
  * @module ui/tabs
3
3
  */
4
4
 
5
- // TODO:
6
- // - For Vertical tabs we should be updating the orientation when on mobile.
7
- // Currently using all arrows so that the interface works in both
8
- // orientations when vertical. Leaving that behavior for now but maybe consider
9
- // setting this up to destroy tab interface when ui layout changes?
10
-
11
- import AriaTablist from "aria-tablist";
5
+ import { TabManager } from "./tab-manager.js";
12
6
  import { ComponentInitializer } from "../core/component.js";
7
+ import { ensureId } from "../utils/id.js";
13
8
 
14
9
  /**
15
10
  * Array of current tab instances (exported if you need to interact with them)
@@ -38,11 +33,8 @@ export function init() {
38
33
  initialize();
39
34
  }
40
35
  });
41
-
42
- // Run this on page load, optionally exported for use when page is running
43
- instances.forEach(openByCurrentHash);
44
36
  };
45
-
37
+
46
38
  if (document.readyState === "complete") {
47
39
  initial();
48
40
  } else {
@@ -51,97 +43,46 @@ export function init() {
51
43
  }
52
44
 
53
45
  /**
54
- *
55
- * @param {Node} element Tablist Element
56
- * @param {Node} options Options to set as defaults (can be overridden by element dataset options)
57
- * @return {Object} Instance object
46
+ * Setup a new TabManager instance
47
+ * @param {HTMLElement} element Tablist Element
48
+ * @param {object} options Options to set as defaults
49
+ * @return {object} Instance object
58
50
  */
59
51
  export function setup(element, options = {}) {
60
- const config = Object.assign({}, options);
52
+ const config = { ...options };
61
53
 
54
+ // Backwards compatibility, `vertical:true` implies `allArrows:true`
62
55
  if (config.vertical) {
63
56
  config.allArrows = true;
64
57
  }
65
58
 
66
- // Need to render the markup before checking height
67
- // - used to wait until images had loaded
68
- const instance = { element, options };
69
- instance.ariaTablist = AriaTablist(element, {
70
- onOpen(...args) {
71
- args.unshift(instance);
72
- handleOpen.apply(null, args);
73
- },
74
- ...config
75
- });
76
- instances.push(instance);
77
-
78
- if (config.equalHeights) {
79
- setHeights(element);
59
+ // Backwards compatibility, `openByUrlHash:true` implies `setUrlHash:true`
60
+ // to replicate the behavior of the old aria-tablist library.
61
+ if (config.openByUrlHash) {
62
+ config.setUrlHash = true;
80
63
  }
81
-
82
- return instance;
83
- }
84
64
 
85
- /**
86
- * Opens the a tabpanel if it matches current hash (used in initial init)
87
- */
88
- function openByCurrentHash({ options, ariaTablist }) {
89
- if (options.openByUrlHash) {
90
- const { hash } = window.location;
91
- if (hash && hash.length > 1) {
92
- const possibleId = hash.substring(1);
93
- ariaTablist.tabs.forEach(tab => {
94
- if (possibleId === tab.id) {
95
- ariaTablist.open(tab);
96
- }
97
- });
65
+ // Backwards compatibility, ensure `aria-controls` is present,
66
+ // generating it from the legacy `aria-labelledby` pattern if needed.
67
+ const tabs = [...element.children];
68
+
69
+ tabs.forEach((tab) => {
70
+ if (!tab.hasAttribute('aria-controls')) {
71
+ // Find the panel using the old association method
72
+ const panel = document.querySelector(`[aria-labelledby="${tab.id}"]`);
73
+ if (panel) {
74
+ ensureId(panel);
75
+ tab.setAttribute('aria-controls', panel.id);
76
+ }
98
77
  }
99
- }
100
- }
78
+ });
101
79
 
102
- /**
103
- * Responsible for setting hash on open if option is set
104
- */
105
- function handleOpen({ options }, panel, tab) {
106
- if (options.openByUrlHash && window.history) {
107
- window.history.replaceState(null, "", `#${ tab.id }`);
108
- }
109
- }
80
+ const instance = { element, options };
110
81
 
111
- /**
112
- * Responsible for creating equal height tab panels
113
- */
114
- function setHeights(element) {
115
- const tabs = [ ...element.children];
116
- const panels = tabs.map(n => document.querySelector(`[aria-labelledby="${ n.id }"]`));
117
- const parent = panels[0].parentElement;
118
- const images = [ ...parent.querySelectorAll("img") ];
119
- const imagePromises = images.map(image => imagePromise(image));
120
- function imagePromise(image) {
121
- return new Promise((resolve) => {
122
- if (image.complete) {
123
- resolve(image);
124
- } else {
125
- image.onload = resolve;
126
- // Errors should also resolve so that height matching continues
127
- image.onerror = resolve;
128
- }
129
- });
130
- }
131
- // Run after images are loaded, or if no images it will resolve and run
132
- Promise.all(imagePromises).then(() => {
133
- const heights = panels.map(panel => {
134
- let panelHeight = panel.offsetHeight;
135
- if (panel.hidden) {
136
- panel.hidden = false;
137
- panelHeight = panel.offsetHeight;
138
- // This explicity needs "hidden" for aria-tablist (it checks this string value)
139
- // Will break the initial window push state when using openWithUrlHash
140
- panel.setAttribute("hidden", "hidden");
141
- }
142
- return panelHeight;
143
- });
144
- const max = Math.max(...heights);
145
- panels.forEach(panel => panel.style.minHeight = `${ max }px`);
146
- });
82
+ // Instantiate the new TabManager. It will find its own tabs/panels
83
+ // and handle all URL hash and equal heights logic internally now
84
+ instance.tabManager = new TabManager(element, config);
85
+ instances.push(instance);
86
+
87
+ return instance;
147
88
  }
@@ -116,13 +116,13 @@ $sizes: (
116
116
  /// @return {Boolean}
117
117
  /// @example scss {compile} Example usage
118
118
  /// .test-exists {
119
- /// @if(ulu.breakpoint-exists("medium")) {
119
+ /// @if (ulu.breakpoint-exists("medium")) {
120
120
  /// @include ulu.breakpoint-min("medium") {
121
121
  /// padding: 2rem;
122
122
  /// }
123
123
  /// }
124
124
  /// // The below content doesn't print because the size doesn't exist.
125
- /// @if(ulu.breakpoint-exists("too-large")) {
125
+ /// @if (ulu.breakpoint-exists("too-large")) {
126
126
  /// @include ulu.breakpoint-min("too-large") {
127
127
  /// padding: 20000rem;
128
128
  /// }
@@ -131,7 +131,7 @@ $sizes: (
131
131
 
132
132
  @function exists($name) {
133
133
  $size: map.get($sizes, $name);
134
- @return if($size != null, true, false);
134
+ @return utils.when($size != null, true, false);
135
135
  }
136
136
 
137
137
  /// Create a media query that matches the min-width for a given size
@@ -296,15 +296,15 @@ $styles: (
296
296
  // If a specific state [hover, active] grab that map
297
297
  @if ($state) {
298
298
  $state-style: map.get($style, $state);
299
- $state-style: if($state-style, $state-style, ());
299
+ $state-style: utils.when($state-style, $state-style, ());
300
300
  }
301
301
  // From is the map to grab styles from
302
- $from: if($state, $state-style, $style);
302
+ $from: utils.when($state, $state-style, $style);
303
303
  $value: map.get($from, $prop);
304
304
 
305
305
  // Fallback to parent (if hover)
306
306
  @if ($state == "hover") {
307
- $value: if($value, $value, map.get($style, $prop));
307
+ $value: utils.when($value, $value, map.get($style, $prop));
308
308
  }
309
309
 
310
310
  @if (meta.type-of($value) == "string" and string.index($prop, "color")) {
@@ -20,7 +20,7 @@ $palette: (
20
20
  "white": white,
21
21
  "type": black,
22
22
  "type-secondary": rgb(82, 82, 82),
23
- "type-tertiary": rgb(125, 125, 125),
23
+ "type-tertiary": rgb(110, 110, 110),
24
24
  "type-disabled": rgb(160, 160, 160),
25
25
  "headline": inherit,
26
26
  "background": white,
@@ -38,6 +38,7 @@ $palette: (
38
38
  "placeholder-background": #e2e2e2,
39
39
  "placeholder-background-alt": #bababa,
40
40
  "selected": green,
41
+ "selected-background": rgb(184, 202, 184),
41
42
  "box-shadow": rgba(0, 0, 0, 0.349),
42
43
  "box-shadow-hover": rgba(0, 0, 0, 0.5),
43
44
  "rule": gray,
@@ -133,7 +134,7 @@ $color-classes: (
133
134
 
134
135
  @function exists($name) {
135
136
  $color: map.get($palette, $name);
136
- @return if($color != null, true, false);
137
+ @return utils.when($color != null, true, false);
137
138
  }
138
139
 
139
140
  /// Set color contexts
@@ -4,6 +4,7 @@
4
4
 
5
5
  @use "sass:map";
6
6
  @use "sass:meta";
7
+
7
8
  @use "color";
8
9
  @use "utils";
9
10
 
@@ -175,7 +176,12 @@ $rule-margins: (
175
176
  /// @param {String} $name ["default"] name of rule style
176
177
 
177
178
  @mixin rule-margin($name: null) {
178
- $margin: if($name, get-rule-margin($name), get("margin"));
179
+ $margin: null;
180
+ @if ($name) {
181
+ $margin: get-rule-margin($name);
182
+ } @else {
183
+ $margin: get("margin");
184
+ }
179
185
  margin-top: $margin;
180
186
  margin-bottom: $margin;
181
187
  }
@@ -337,7 +343,7 @@ $rule-margins: (
337
343
  "padding-adjust" : null,
338
344
  );
339
345
  $config: map.merge($defaults, $options);
340
- $element: if($before, "::before", "::after");
346
+ $element: utils.when($before, "::before", "::after");
341
347
 
342
348
  &#{ $element } {
343
349
  content: "";
@@ -362,14 +368,14 @@ $rule-margins: (
362
368
  $options: (),
363
369
  $before: true
364
370
  ) {
365
- $element: if($before, "::before", "::after");
371
+ $element: utils.when($before, "::before", "::after");
366
372
  $size: map.get($options, "size");
367
373
  $offset: map.get($options, "offset");
368
374
  $border-radius: map.get($options, "border-radius");
369
375
  $padding-adjust: map.get($options, "padding-adjust");
370
376
 
371
377
  $end: $side == "top" or $side == "bottom";
372
- $position: if($offset, 0 - $offset, null);
378
+ $position: utils.when($offset, 0 - $offset, null);
373
379
 
374
380
  @if ($padding-adjust and $size) {
375
381
  padding-#{ $side }: calc($padding-adjust + $size);