@andreyshpigunov/x 0.3.89 → 0.4.0

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 (64) hide show
  1. package/README.md +1 -1
  2. package/assets/css/app.css +1 -0
  3. package/assets/img/github-mark-white.png +0 -0
  4. package/assets/img/github-mark.png +0 -0
  5. package/assets/js/app.js +1 -0
  6. package/cheatsheet.html +427 -0
  7. package/dist/x.css +1 -1
  8. package/dist/x.js +1 -3
  9. package/index.html +20 -24
  10. package/package.json +52 -47
  11. package/postcss.config.cjs +0 -2
  12. package/src/components/x/animate.js +39 -45
  13. package/src/components/x/appear.js +19 -26
  14. package/src/components/x/autocomplete.js +22 -10
  15. package/src/components/x/buttons.css +40 -16
  16. package/src/components/x/colors.css +47 -41
  17. package/src/components/x/debug.css +2 -2
  18. package/src/components/x/device.js +39 -33
  19. package/src/components/x/dropdown.css +2 -3
  20. package/src/components/x/dropdown.js +16 -9
  21. package/src/components/x/flex.css +146 -109
  22. package/src/components/x/flow.css +12 -6
  23. package/src/components/x/form.css +3 -3
  24. package/src/components/x/form.js +12 -9
  25. package/src/components/x/grid.css +78 -42
  26. package/src/components/x/helpers.css +601 -438
  27. package/src/components/x/hover.js +20 -9
  28. package/src/components/x/icons.css +12 -12
  29. package/src/components/x/lazyload.js +17 -8
  30. package/src/components/x/lib.js +15 -1
  31. package/src/components/x/links.css +2 -6
  32. package/src/components/x/loadmore.js +17 -5
  33. package/src/components/x/modal.css +4 -22
  34. package/src/components/x/modal.js +14 -5
  35. package/src/components/x/reset.css +1 -15
  36. package/src/components/x/scroll.css +4 -9
  37. package/src/components/x/scroll.js +14 -1
  38. package/src/components/x/sheets.css +0 -3
  39. package/src/components/x/sheets.js +157 -37
  40. package/src/components/x/slider.css +10 -1
  41. package/src/components/x/slider.js +15 -0
  42. package/src/components/x/space.css +22 -2
  43. package/src/components/x/sticky.css +10 -15
  44. package/src/components/x/sticky.js +21 -4
  45. package/src/components/x/typo.css +14 -40
  46. package/src/css/app.css +7 -8
  47. package/src/css/x.css +191 -213
  48. package/src/js/app.js +8 -8
  49. package/src/js/x.js +37 -41
  50. package/assets/github-mark-white.png +0 -0
  51. package/assets/github-mark.png +0 -0
  52. package/assets/logo-inverse.png +0 -0
  53. package/babel.config.cjs +0 -4
  54. package/dist/app.css +0 -1
  55. package/dist/app.js +0 -1
  56. package/dist/index.html +0 -2182
  57. package/dist/logo.png +0 -0
  58. package/jest.config.mjs +0 -7
  59. package/jsdoc.json +0 -11
  60. package/vite.config.js +0 -31
  61. /package/assets/{alpha.png → img/alpha.png} +0 -0
  62. /package/assets/{apple-touch-icon.png → img/apple-touch-icon.png} +0 -0
  63. /package/assets/{logo.png → img/logo.png} +0 -0
  64. /package/assets/{logo.svg → img/logo.svg} +0 -0
@@ -51,21 +51,38 @@
51
51
  * <div x-sheet="b1">Content B1</div>
52
52
  * </div>
53
53
  *
54
+ * Events:
55
+ * const sheetsContainer = document.querySelector('#mySheets');
56
+ *
57
+ * sheetsContainer.addEventListener('sheets:ready', (e) => {
58
+ * console.log('Sheets initialized', e.detail);
59
+ * });
60
+ *
61
+ * sheetsContainer.addEventListener('sheets:selected', (e) => {
62
+ * console.log('Tab selected:', e.detail.sheet);
63
+ * });
64
+ *
65
+ * sheetsContainer.addEventListener('sheets:destroy', (e) => {
66
+ * console.log('Sheets destroyed', e.detail);
67
+ * });
68
+ *
54
69
  * Public API:
55
70
  *
56
71
  * @method init() - Initializes all [x-sheets] components on the page.
57
72
  * Binds click events to [x-sheet-open] elements and activates tabs with .active class.
58
73
  * Safe for multiple calls - automatically destroys previous listeners.
74
+ * Dispatches 'sheets:ready' event on each container when done.
59
75
  * @example
60
76
  * sheets.init();
61
77
  *
62
78
  * @method destroy() - Removes all sheets-related event listeners and resets state.
63
- * Safe to call multiple times.
79
+ * Safe to call multiple times. Dispatches 'sheets:destroy' event on each container.
64
80
  * @example
65
81
  * sheets.destroy();
66
82
  *
67
83
  * @method show(xSheet) - Programmatically switches to a specific sheet.
68
84
  * Activates the tab and corresponding content block by matching x-sheet value.
85
+ * Dispatches 'sheets:selected' event on the container when tab changes.
69
86
  * @param {string} xSheet - The value of x-sheet and x-sheet-open to activate.
70
87
  * @example
71
88
  * sheets.show('tab2');
@@ -108,8 +125,26 @@
108
125
  * - Test nested sheets behavior
109
126
  * - Handle errors gracefully
110
127
  *
128
+ * @example
129
+ * // Vanilla JS — plain HTML
130
+ * // index.html:
131
+ * // <div x-sheets id="mySheets">
132
+ * // <nav>
133
+ * // <button type="button" x-sheet-open="tab1" class="active">Tab 1</button>
134
+ * // <button type="button" x-sheet-open="tab2">Tab 2</button>
135
+ * // </nav>
136
+ * // <section x-sheet="tab1" class="active">Content 1</section>
137
+ * // <section x-sheet="tab2">Content 2</section>
138
+ * // </div>
139
+ * //
140
+ * // <script type="module">
141
+ * // import { sheets } from './src/components/x/sheets.js';
142
+ * // window.addEventListener('DOMContentLoaded', () => sheets.init());
143
+ * // window.addEventListener('pagehide', () => sheets.destroy());
144
+ * // </script>
145
+ *
111
146
  * @author Andrey Shpigunov
112
- * @version 0.3
147
+ * @version 0.4
113
148
  * @since 2025-07-18
114
149
  */
115
150
 
@@ -130,7 +165,7 @@ class Sheets {
130
165
  * @private
131
166
  */
132
167
  this._handlers = new Map();
133
-
168
+
134
169
  /**
135
170
  * Initialization flag to control safe reinitialization.
136
171
  * @type {boolean}
@@ -138,7 +173,23 @@ class Sheets {
138
173
  */
139
174
  this._initialized = false;
140
175
  }
141
-
176
+
177
+ /**
178
+ * Dispatch custom event on specific container
179
+ * @param {HTMLElement} container - The x-sheets container element
180
+ * @param {string} eventName - Event name (without 'sheets:' prefix)
181
+ * @param {Object} detail - Event detail data
182
+ * @private
183
+ */
184
+ _dispatchEvent(container, eventName, detail = {}) {
185
+ const event = new CustomEvent(`sheets:${eventName}`, {
186
+ detail,
187
+ bubbles: true,
188
+ cancelable: false
189
+ });
190
+ container.dispatchEvent(event);
191
+ }
192
+
142
193
  /**
143
194
  * Validates sheet value to prevent XSS attacks.
144
195
  * Only allows alphanumeric characters, hyphens, and underscores.
@@ -150,7 +201,7 @@ class Sheets {
150
201
  if (typeof value !== 'string' || !value) return false;
151
202
  return /^[a-zA-Z0-9_-]+$/.test(value);
152
203
  }
153
-
204
+
154
205
  /**
155
206
  * Initializes all `[x-sheets]` components on the page.
156
207
  *
@@ -158,44 +209,47 @@ class Sheets {
158
209
  * - Activates the tab with `.active` class by default.
159
210
  * - SECURITY: Validates sheet values before use.
160
211
  * - Safe for multiple calls; previous listeners are removed.
212
+ * - Dispatches 'sheets:ready' event on each container when initialization is complete.
161
213
  */
162
214
  init() {
163
215
  if (this._initialized) {
164
216
  this.destroy();
165
217
  }
166
-
218
+
167
219
  const sheets = lib.qsa('[x-sheets]');
168
- if (!sheets.length) return;
169
-
220
+ if (!sheets.length) {
221
+ return;
222
+ }
223
+
170
224
  for (const sheet of sheets) {
171
225
  if (!sheet) continue;
172
-
226
+
173
227
  const tabs = lib.qsa('[x-sheet-open]:not([x-sheet-open] [x-sheet-open])', sheet);
174
-
228
+
175
229
  for (const tab of tabs) {
176
230
  if (!tab) continue;
177
-
231
+
178
232
  const sheetValue = tab.getAttribute('x-sheet-open');
179
233
  if (!sheetValue) {
180
234
  console.warn('sheets.init: Tab has x-sheet-open attribute but no value', tab);
181
235
  continue;
182
236
  }
183
-
237
+
184
238
  // SECURITY: Validate sheet value
185
239
  if (!this._isValidSheetValue(sheetValue)) {
186
240
  console.error('sheets.init: Invalid sheet value (security check failed):', sheetValue);
187
241
  continue;
188
242
  }
189
-
243
+
190
244
  const handler = (e) => {
191
245
  e.preventDefault();
192
246
  this.show(sheetValue);
193
247
  };
194
-
248
+
195
249
  tab.addEventListener('click', handler);
196
250
  this._handlers.set(tab, handler);
197
251
  }
198
-
252
+
199
253
  const active = lib.qs('[x-sheet-open].active', sheet);
200
254
  if (active) {
201
255
  const activeValue = active.getAttribute('x-sheet-open');
@@ -203,91 +257,157 @@ class Sheets {
203
257
  this.show(activeValue);
204
258
  }
205
259
  }
260
+
261
+ // Dispatch ready event on this container
262
+ this._dispatchEvent(sheet, 'ready', {
263
+ container: sheet,
264
+ timestamp: Date.now(),
265
+ initialized: true
266
+ });
206
267
  }
207
-
268
+
208
269
  this._initialized = true;
209
270
  }
210
-
271
+
211
272
  /**
212
273
  * Removes all event listeners and resets internal state.
213
- * Safe to call multiple times.
274
+ * Safe to call multiple times. Dispatches 'sheets:destroy' event on each container.
214
275
  */
215
276
  destroy() {
277
+ // Store containers before clearing handlers
278
+ const containers = new Set();
279
+ for (const [el] of this._handlers.entries()) {
280
+ const container = el.closest('[x-sheets]');
281
+ if (container) {
282
+ containers.add(container);
283
+ }
284
+ }
285
+
286
+ // Remove all event listeners
216
287
  for (const [el, handler] of this._handlers.entries()) {
217
288
  el.removeEventListener('click', handler);
218
289
  }
219
-
290
+
220
291
  this._handlers.clear();
221
292
  this._initialized = false;
293
+
294
+ // Dispatch destroy event on each container
295
+ for (const container of containers) {
296
+ this._dispatchEvent(container, 'destroy', {
297
+ container: container,
298
+ timestamp: Date.now(),
299
+ wasInitialized: true
300
+ });
301
+ }
222
302
  }
223
-
303
+
224
304
  /**
225
305
  * Activates a tab and its corresponding content block by `x-sheet` key.
226
306
  * SECURITY: Validates sheet value and uses CSS.escape() for selectors.
307
+ * Dispatches 'sheets:selected' event on the container when tab changes.
227
308
  *
228
309
  * @param {string} xSheet - The value of `x-sheet` and `x-sheet-open` to activate.
310
+ * @param {Object} options - Optional configuration.
311
+ * @param {boolean} options.silent - If true, prevents dispatching 'sheets:selected' event.
312
+ * @param {string} options.source - Source of the selection ('api' or 'user').
229
313
  */
230
- show(xSheet) {
314
+ show(xSheet, options = { silent: false, source: 'api' }) {
231
315
  if (!xSheet || typeof xSheet !== 'string') {
232
316
  console.error('sheets.show: Sheet value is required and must be a string');
233
317
  return;
234
318
  }
235
-
319
+
236
320
  // SECURITY: Validate sheet value
237
321
  if (!this._isValidSheetValue(xSheet)) {
238
322
  console.error('sheets.show: Invalid sheet value (security check failed):', xSheet);
239
323
  return;
240
324
  }
241
-
325
+
242
326
  // SECURITY: Use CSS.escape() to prevent XSS in selectors
243
327
  const escapedValue = CSS.escape(xSheet);
244
328
  const targetBody = lib.qs(`[x-sheet="${escapedValue}"]`);
245
-
329
+
246
330
  if (!targetBody) {
247
331
  console.warn('sheets.show: Target content not found:', xSheet);
248
332
  return;
249
333
  }
250
-
251
- const sheets = targetBody.closest('[x-sheets]');
252
- if (!sheets) {
334
+
335
+ const container = targetBody.closest('[x-sheets]');
336
+ if (!container) {
253
337
  console.warn('sheets.show: Sheets container not found for:', xSheet);
254
338
  return;
255
339
  }
256
-
257
- const selectedTab = lib.qs(`[x-sheet-open="${escapedValue}"]`, sheets);
258
-
340
+
341
+ const selectedTab = lib.qs(`[x-sheet-open="${escapedValue}"]`, container);
342
+
259
343
  if (!selectedTab) {
260
344
  console.warn('sheets.show: Tab not found:', xSheet);
261
345
  return;
262
346
  }
263
-
347
+
264
348
  // Check if already active
265
349
  if (
266
350
  selectedTab.classList.contains('active') &&
267
351
  targetBody.classList.contains('active')
268
352
  ) {
353
+ // Dispatch selected event even if already active (for consistency)
354
+ if (!options.silent) {
355
+ this._dispatchEvent(container, 'selected', {
356
+ sheet: xSheet,
357
+ tab: selectedTab,
358
+ content: targetBody,
359
+ container: container,
360
+ previousSheet: xSheet,
361
+ previousTab: selectedTab,
362
+ previousContent: targetBody,
363
+ wasActive: true,
364
+ source: options.source || 'api',
365
+ timestamp: Date.now()
366
+ });
367
+ }
269
368
  return; // Already active
270
369
  }
271
-
370
+
371
+ // Get previous active tab and body for event data
372
+ const previousTab = lib.qs('[x-sheet-open].active', container);
373
+ const previousBody = lib.qs('[x-sheet].active', container);
374
+ const previousSheet = previousTab ? previousTab.getAttribute('x-sheet-open') : null;
375
+
272
376
  // Remove active class from all tabs and bodies in this container
273
- const tabs = lib.qsa('[x-sheet-open]', sheets);
274
- const bodies = lib.qsa('[x-sheet]', sheets);
275
-
377
+ const tabs = lib.qsa('[x-sheet-open]', container);
378
+ const bodies = lib.qsa('[x-sheet]', container);
379
+
276
380
  for (const tab of tabs) {
277
381
  if (tab && tab.classList.contains('active')) {
278
382
  tab.classList.remove('active');
279
383
  }
280
384
  }
281
-
385
+
282
386
  for (const body of bodies) {
283
387
  if (body && body.classList.contains('active')) {
284
388
  body.classList.remove('active');
285
389
  }
286
390
  }
287
-
391
+
288
392
  // Add active class to selected tab and body
289
393
  selectedTab.classList.add('active');
290
394
  targetBody.classList.add('active');
395
+
396
+ // Dispatch selected event on the container
397
+ if (!options.silent) {
398
+ this._dispatchEvent(container, 'selected', {
399
+ sheet: xSheet,
400
+ tab: selectedTab,
401
+ content: targetBody,
402
+ container: container,
403
+ previousSheet: previousSheet,
404
+ previousTab: previousTab,
405
+ previousContent: previousBody,
406
+ wasActive: false,
407
+ source: options.source || 'api',
408
+ timestamp: Date.now()
409
+ });
410
+ }
291
411
  }
292
412
  }
293
413
 
@@ -1,3 +1,12 @@
1
+ /*----------------------------------------
2
+ slider.css
3
+ Slider
4
+
5
+ Created by Andrey Shpigunov at 13.04.2026
6
+ All right reserved.
7
+ ----------------------------------------*/
8
+
9
+
1
10
  :root {
2
11
  --slider-point-color: #ffffff66;
3
12
  --slider-point-color-active: #fff;
@@ -22,7 +31,7 @@
22
31
  overscroll-behavior: contain; /* не отдаём overscroll родителю */
23
32
  }
24
33
  .slider_touch {
25
- touch-action: pan-y !important; /* разрешаем только вертикальные жесты браузеру */
34
+ touch-action: pan-y; /* разрешаем только вертикальные жесты браузеру */
26
35
  }
27
36
  .slider-wrapper {
28
37
  display: flex;
@@ -122,6 +122,21 @@
122
122
  * - Touch events support for mobile devices
123
123
  * - CSS transforms and transitions
124
124
  *
125
+ * @example
126
+ * // Vanilla JS — plain HTML
127
+ * // index.html:
128
+ * // <div x-slider='{"gap":16,"rubber":true,"resetOnMouseout":true,"touch":true}'>
129
+ * // <div><img data-src="slide1.jpg" alt="Slide 1"></div>
130
+ * // <div><img data-src="slide2.jpg" alt="Slide 2"></div>
131
+ * // <div><img data-src="slide3.jpg" alt="Slide 3"></div>
132
+ * // </div>
133
+ * //
134
+ * // <script type="module">
135
+ * // import { slider } from './src/components/x/slider.js';
136
+ * // window.addEventListener('DOMContentLoaded', () => slider.init());
137
+ * // window.addEventListener('pagehide', () => slider.destroy());
138
+ * // </script>
139
+ *
125
140
  * @author Andrey Shpigunov
126
141
  * @version 0.3
127
142
  * @since 2025-07-18
@@ -7,13 +7,25 @@ All right reserved.
7
7
  ----------------------------------------*/
8
8
 
9
9
 
10
+ /*
11
+ .space[0-10] (s,m,l,xl) - vertical space
12
+ */
13
+
10
14
  @for $i from 0 to 10 {
11
15
  .space$(i) {
12
16
  height: var(--space-$(i));
13
17
  }
14
18
  }
15
19
 
16
- @media (--medium) {
20
+ @media (min-width: 640px) {
21
+ @for $i from 0 to 10 {
22
+ .s\:space$(i) {
23
+ height: var(--space-$(i));
24
+ }
25
+ }
26
+ }
27
+
28
+ @media (min-width: 768px) {
17
29
  @for $i from 0 to 10 {
18
30
  .m\:space$(i) {
19
31
  height: var(--space-$(i));
@@ -21,10 +33,18 @@ All right reserved.
21
33
  }
22
34
  }
23
35
 
24
- @media (--large) {
36
+ @media (min-width: 1024px) {
25
37
  @for $i from 0 to 10 {
26
38
  .l\:space$(i) {
27
39
  height: var(--space-$(i));
28
40
  }
29
41
  }
30
42
  }
43
+
44
+ @media (min-width: 1280px) {
45
+ @for $i from 0 to 10 {
46
+ .xl\:space$(i) {
47
+ height: var(--space-$(i));
48
+ }
49
+ }
50
+ }
@@ -7,25 +7,20 @@ All right reserved.
7
7
  ----------------------------------------*/
8
8
 
9
9
 
10
- /* !- Sticky */
11
-
12
-
13
- /* .sticky (m,l,xl) */
14
-
10
+ /* .sticky (s,m,l,xl) */
15
11
 
16
12
  .sticky {
17
- position: sticky !important;
18
- top: 0;
13
+ position: sticky;
19
14
  }
20
- @media (--medium) {
21
- .s\:sticky { position: sticky !important; top: 0 }
15
+ @media (max-width: 640px) {
16
+ .s\:sticky { position: sticky }
22
17
  }
23
- @media (--medium) {
24
- .m\:sticky { position: sticky !important; top: 0 }
18
+ @media (min-width: 768px) {
19
+ .m\:sticky { position: sticky }
25
20
  }
26
- @media (--large) {
27
- .l\:sticky { position: sticky !important; top: 0 }
21
+ @media (min-width: 1024px) {
22
+ .l\:sticky { position: sticky }
28
23
  }
29
- @media (--xlarge) {
30
- .xl\:sticky { position: sticky !important; top: 0 }
24
+ @media (min-width: 1280px) {
25
+ .xl\:sticky { position: sticky }
31
26
  }
@@ -23,7 +23,7 @@
23
23
  * top: 0;
24
24
  * z-index: 100;
25
25
  * }
26
- * .sticky.sticky_on {
26
+ * .sticky.sticky--on {
27
27
  * box-shadow: 0 2px 4px rgba(0,0,0,0.1);
28
28
  * }
29
29
  *
@@ -76,7 +76,7 @@
76
76
  * CSS classes:
77
77
  *
78
78
  * - `.sticky` - Required class to mark element for observation
79
- * - `.sticky_on` - Added when element is in sticky state (not fully visible)
79
+ * - `.sticky--on` - Added when element is in sticky state (not fully visible)
80
80
  * - Removed when element returns to normal flow
81
81
  *
82
82
  * How it works:
@@ -85,7 +85,7 @@
85
85
  * 2. Uses threshold: 1 (element must be fully visible)
86
86
  * 3. Uses rootMargin: '-1px 0px 0px 0px' (triggers when top edge leaves viewport)
87
87
  * 4. When intersectionRatio < 1, element is sticky
88
- * 5. Toggles `sticky_on` class and dispatches events
88
+ * 5. Toggles `sticky--on` class and dispatches events
89
89
  *
90
90
  * Observer configuration:
91
91
  *
@@ -114,6 +114,23 @@
114
114
  * - Modern browsers (Chrome 51+, Firefox 55+, Safari 12.1+, Edge 15+)
115
115
  * - Falls back gracefully if not supported (no errors, just no functionality)
116
116
  *
117
+ * @example
118
+ * // Vanilla JS — plain HTML
119
+ * // index.html:
120
+ * // <header class="sticky"><nav>Navigation</nav></header>
121
+ * // <main style="min-height: 200vh">Scroll content</main>
122
+ * //
123
+ * // <style>
124
+ * // .sticky { position: sticky; top: 0; z-index: 100; }
125
+ * // .sticky.sticky--on { box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
126
+ * // </style>
127
+ * //
128
+ * // <script type="module">
129
+ * // import { sticky } from './src/components/x/sticky.js';
130
+ * // window.addEventListener('DOMContentLoaded', () => sticky.init());
131
+ * // window.addEventListener('pagehide', () => sticky.destroy());
132
+ * // </script>
133
+ *
117
134
  * @author Andrey Shpigunov
118
135
  * @version 0.2
119
136
  * @since 2025-07-17
@@ -133,7 +150,7 @@ class Sticky {
133
150
  * Class to apply when sticky state is active.
134
151
  * @type {string}
135
152
  */
136
- this.activeClass = 'sticky_on';
153
+ this.activeClass = 'sticky--on';
137
154
 
138
155
  /**
139
156
  * Root margin for IntersectionObserver.
@@ -7,9 +7,6 @@ All right reserved.
7
7
  ----------------------------------------*/
8
8
 
9
9
 
10
- /* !- Typo */
11
-
12
-
13
10
  /*
14
11
  .h[1-6]
15
12
  .mono
@@ -22,7 +19,6 @@ All right reserved.
22
19
  .lh[0-9] (m,l,xl) - values: 1.0-1.9
23
20
  */
24
21
 
25
-
26
22
  :root {
27
23
  --headers-margin-top: 1em;
28
24
  --headers-margin-bottom: .5em;
@@ -145,15 +141,15 @@ pre {
145
141
 
146
142
  /* !- Lists */
147
143
 
148
- ul,
149
- ol,
144
+ ul,
145
+ ol,
150
146
  dl {
151
147
  margin: var(--paragraph-margin) 0;
152
148
  padding-left: calc(var(--paragraph-margin) * 1.5);
153
149
 
154
- & ul,
155
- & ol,
156
- & dl,
150
+ & ul,
151
+ & ol,
152
+ & dl,
157
153
  & li {
158
154
  margin: var(--space-1) 0;
159
155
  }
@@ -210,19 +206,7 @@ table {
210
206
  .fw$(i) { font-weight: $(i) }
211
207
  }
212
208
 
213
- @media (--small) {
214
- @for $i from 10 to 19 {
215
- .s\:fs$(i) { font-size: calc($(i)rem / 10) }
216
- }
217
- @for $i from 20 to 64 by 2 {
218
- .s\:fs$(i) { font-size: calc($(i)rem / 10) }
219
- }
220
- @for $i from 100 to 900 by 100 {
221
- .s\:fw$(i) { font-weight: $(i) }
222
- }
223
- }
224
-
225
- @media (--medium) {
209
+ @media (min-width: 768px) {
226
210
  @for $i from 10 to 19 {
227
211
  .m\:fs$(i) { font-size: calc($(i)rem / 10) }
228
212
  }
@@ -234,7 +218,7 @@ table {
234
218
  }
235
219
  }
236
220
 
237
- @media (--large) {
221
+ @media (min-width: 1024px) {
238
222
  @for $i from 10 to 19 {
239
223
  .l\:fs$(i) { font-size: calc($(i)rem / 10) }
240
224
  }
@@ -246,7 +230,7 @@ table {
246
230
  }
247
231
  }
248
232
 
249
- @media (--xlarge) {
233
+ @media (min-width: 1280px) {
250
234
  @for $i from 10 to 19 {
251
235
  .xl\:fs$(i) { font-size: calc($(i)rem / 10) }
252
236
  }
@@ -265,22 +249,17 @@ table {
265
249
  .ls$(i) { letter-spacing: calc($(i)em * 0.625) }
266
250
  }
267
251
 
268
- @media (--small) {
269
- @for $i from 0 to 4 {
270
- .s\:ls$(i) { letter-spacing: calc($(i)em * 0.625) }
271
- }
272
- }
273
- @media (--medium) {
252
+ @media (min-width: 768px) {
274
253
  @for $i from 0 to 4 {
275
254
  .m\:ls$(i) { letter-spacing: calc($(i)em * 0.625) }
276
255
  }
277
256
  }
278
- @media (--large) {
257
+ @media (min-width: 1024px) {
279
258
  @for $i from 0 to 4 {
280
259
  .l\:ls$(i) { letter-spacing: calc($(i)em * 0.625) }
281
260
  }
282
261
  }
283
- @media (--xlarge) {
262
+ @media (min-width: 1280px) {
284
263
  @for $i from 0 to 4 {
285
264
  .xl\:ls$(i) { letter-spacing: calc($(i)em * 0.625) }
286
265
  }
@@ -293,22 +272,17 @@ table {
293
272
  .lh$(i) { line-height: 1.$(i) }
294
273
  }
295
274
 
296
- @media (--small) {
297
- @for $i from 0 to 9 {
298
- .s\:lh$(i) { line-height: 1.$(i) }
299
- }
300
- }
301
- @media (--medium) {
275
+ @media (min-width: 768px) {
302
276
  @for $i from 0 to 9 {
303
277
  .m\:lh$(i) { line-height: 1.$(i) }
304
278
  }
305
279
  }
306
- @media (--large) {
280
+ @media (min-width: 1024px) {
307
281
  @for $i from 0 to 9 {
308
282
  .l\:lh$(i) { line-height: 1.$(i) }
309
283
  }
310
284
  }
311
- @media (--xlarge) {
285
+ @media (min-width: 1280px) {
312
286
  @for $i from 0 to 9 {
313
287
  .xl\:lh$(i) { line-height: 1.$(i) }
314
288
  }