@orioncactuscorp/ui 1.1.0 → 1.3.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 (95) hide show
  1. package/README.md +82 -26
  2. package/dist/components/Accordion/Accordion.css +1 -1
  3. package/dist/components/Accordion/Accordion.js +39 -38
  4. package/dist/components/Accordion/Accordion.module.scss.js +5 -5
  5. package/dist/components/ActionArea/ActionArea.css +1 -1
  6. package/dist/components/ActionArea/ActionArea.js +101 -71
  7. package/dist/components/Alert/Alert.css +1 -1
  8. package/dist/components/BackgroundIconButton/BackgroundIconButton.css +1 -1
  9. package/dist/components/Badge/Badge.css +1 -1
  10. package/dist/components/Badge/Badge.js +69 -25
  11. package/dist/components/Button/Button.css +1 -1
  12. package/dist/components/Button/Button.js +27 -27
  13. package/dist/components/Cell/Cell.css +1 -1
  14. package/dist/components/Cell/Cell.js +98 -88
  15. package/dist/components/CheckMark/CheckMark.css +1 -1
  16. package/dist/components/CheckMark/CheckMark.js +20 -18
  17. package/dist/components/CheckMark/CheckMark.module.scss.js +7 -3
  18. package/dist/components/Checkbox/Checkbox.css +1 -1
  19. package/dist/components/Checkbox/Checkbox.module.scss.js +10 -8
  20. package/dist/components/Field/Field.css +1 -1
  21. package/dist/components/Fieldset/Fieldset.css +1 -1
  22. package/dist/components/IconButton/IconButton.css +1 -1
  23. package/dist/components/Interaction/Interaction.css +1 -1
  24. package/dist/components/Loading/Loading.css +1 -1
  25. package/dist/components/Menu/Menu.css +1 -1
  26. package/dist/components/Modal/Modal.css +1 -1
  27. package/dist/components/Modal/Modal.js +377 -346
  28. package/dist/components/Modal/useModalDrag.js +654 -614
  29. package/dist/components/Radio/Radio.css +1 -1
  30. package/dist/components/Radio/Radio.module.scss.js +9 -7
  31. package/dist/components/ScrollArea/ScrollArea.css +1 -1
  32. package/dist/components/SectionHeader/SectionHeader.css +1 -1
  33. package/dist/components/Select/Select.css +1 -1
  34. package/dist/components/Select/Select.js +15 -15
  35. package/dist/components/Select/SelectContent.css +1 -1
  36. package/dist/components/Switch/Switch.css +1 -1
  37. package/dist/components/Switch/Switch.module.scss.js +6 -4
  38. package/dist/components/TextArea/TextArea.css +1 -1
  39. package/dist/components/TextArea/TextAreaContent.css +1 -1
  40. package/dist/components/TextButton/TextButton.css +1 -1
  41. package/dist/components/TextInput/TextInput.css +1 -1
  42. package/dist/components/TextInput/TextInputContent.css +1 -1
  43. package/dist/components/Tooltip/Tooltip.css +1 -1
  44. package/dist/foundations/atomic.css +1 -1
  45. package/dist/foundations/motion.css +1 -1
  46. package/dist/foundations/typo.css +1 -1
  47. package/dist/foundations.css +3 -3
  48. package/dist/react/motion.js +109 -106
  49. package/dist/styles.css +31 -31
  50. package/dist/ui/src/components/Accordion/Accordion.d.ts.map +1 -1
  51. package/dist/ui/src/components/ActionArea/ActionArea.d.ts +1 -1
  52. package/dist/ui/src/components/ActionArea/ActionArea.d.ts.map +1 -1
  53. package/dist/ui/src/components/ActionArea/index.d.ts +1 -1
  54. package/dist/ui/src/components/ActionArea/index.d.ts.map +1 -1
  55. package/dist/ui/src/components/ActionArea/types.d.ts +2 -2
  56. package/dist/ui/src/components/ActionArea/types.d.ts.map +1 -1
  57. package/dist/ui/src/components/Badge/Badge.d.ts +1 -1
  58. package/dist/ui/src/components/Badge/Badge.d.ts.map +1 -1
  59. package/dist/ui/src/components/Badge/types.d.ts +19 -2
  60. package/dist/ui/src/components/Badge/types.d.ts.map +1 -1
  61. package/dist/ui/src/components/Button/Button.d.ts.map +1 -1
  62. package/dist/ui/src/components/Cell/Cell.d.ts.map +1 -1
  63. package/dist/ui/src/components/CheckMark/CheckMark.d.ts +1 -1
  64. package/dist/ui/src/components/CheckMark/CheckMark.d.ts.map +1 -1
  65. package/dist/ui/src/components/CheckMark/index.d.ts +1 -1
  66. package/dist/ui/src/components/CheckMark/index.d.ts.map +1 -1
  67. package/dist/ui/src/components/CheckMark/types.d.ts +2 -0
  68. package/dist/ui/src/components/CheckMark/types.d.ts.map +1 -1
  69. package/dist/ui/src/components/Modal/Modal.d.ts.map +1 -1
  70. package/dist/ui/src/components/Modal/contexts.d.ts +1 -0
  71. package/dist/ui/src/components/Modal/contexts.d.ts.map +1 -1
  72. package/dist/ui/src/components/Modal/types.d.ts +5 -4
  73. package/dist/ui/src/components/Modal/types.d.ts.map +1 -1
  74. package/dist/ui/src/components/Modal/useModalDrag.d.ts.map +1 -1
  75. package/dist/ui/src/index.d.ts +4 -4
  76. package/dist/ui/src/index.d.ts.map +1 -1
  77. package/dist/ui/src/react/motion/index.d.ts +9 -8
  78. package/dist/ui/src/react/motion/index.d.ts.map +1 -1
  79. package/dist/ui/src/utils/motion.d.ts +15 -14
  80. package/dist/ui/src/utils/motion.d.ts.map +1 -1
  81. package/dist/utils/motion.js +282 -183
  82. package/dist/vscode/oc-ui-vars.css +31 -16
  83. package/package.json +2 -2
  84. package/src/scss/foundations/atomic.scss +12 -10
  85. package/src/scss/foundations/atomic.test.ts +34 -0
  86. package/src/scss/foundations/motion.scss +30 -16
  87. package/src/scss/foundations/typo.scss +1 -1
  88. package/src/scss/index.scss +1 -0
  89. package/src/scss/mixins/_element.scss +67 -7
  90. package/src/scss/mixins/_element.test.ts +94 -7
  91. package/src/scss/mixins/_motion.scss +168 -77
  92. package/src/scss/mixins/_motion.test.ts +137 -54
  93. package/src/scss/mixins/_radius.scss +3 -0
  94. package/src/scss/mixins/_radius.test.ts +52 -0
  95. package/src/scss/mixins/_typo.scss +2 -2
@@ -4,6 +4,7 @@
4
4
  :root {
5
5
  --oc-atomic-blur-compact: initial;
6
6
  --oc-atomic-blur-normal: initial;
7
+ --oc-atomic-radius-factor: initial;
7
8
  --oc-atomic-radius-huge: initial;
8
9
  --oc-atomic-radius-large: initial;
9
10
  --oc-atomic-radius-max: initial;
@@ -291,34 +292,48 @@
291
292
  --oc-color-theme-status-cautionary: initial;
292
293
  --oc-color-theme-status-negative: initial;
293
294
  --oc-color-theme-status-positive: initial;
294
- --oc-motion-distance-scale: initial;
295
- --oc-motion-distance-scale-reject: initial;
296
- --oc-motion-distance-scale-reveal: initial;
295
+ --oc-motion-distance-factor: initial;
296
+ --oc-motion-distance-factor-reject: initial;
297
+ --oc-motion-distance-factor-spatial: initial;
297
298
  --oc-motion-duration-ambient: initial;
299
+ --oc-motion-duration-disclosure: initial;
300
+ --oc-motion-duration-disclosure-exit: initial;
301
+ --oc-motion-duration-expand: initial;
302
+ --oc-motion-duration-expand-exit: initial;
303
+ --oc-motion-duration-factor: initial;
304
+ --oc-motion-duration-factor-preserve: initial;
305
+ --oc-motion-duration-factor-reduced: initial;
298
306
  --oc-motion-duration-feedback: initial;
299
307
  --oc-motion-duration-gesture: initial;
300
- --oc-motion-duration-gesture-settle: initial;
308
+ --oc-motion-duration-gesture-release: initial;
301
309
  --oc-motion-duration-move: initial;
302
310
  --oc-motion-duration-reject: initial;
303
- --oc-motion-duration-reveal: initial;
304
- --oc-motion-duration-reveal-exit: initial;
305
- --oc-motion-duration-scale: initial;
306
- --oc-motion-duration-scale-preserve: initial;
307
- --oc-motion-duration-scale-reduced: initial;
311
+ --oc-motion-duration-sheet: initial;
312
+ --oc-motion-duration-sheet-exit: initial;
313
+ --oc-motion-duration-sheet-release: initial;
314
+ --oc-motion-duration-surface: initial;
315
+ --oc-motion-duration-surface-exit: initial;
308
316
  --oc-motion-pace-normal: initial;
309
317
  --oc-motion-pace-quick: initial;
310
318
  --oc-motion-pace-slow: initial;
311
- --oc-motion-scale: initial;
312
- --oc-motion-scale-feedback: initial;
313
- --oc-motion-scale-reveal: initial;
319
+ --oc-motion-scale-factor: initial;
320
+ --oc-motion-scale-factor-feedback: initial;
321
+ --oc-motion-scale-factor-spatial: initial;
314
322
  --oc-motion-timing-ambient: initial;
323
+ --oc-motion-timing-disclosure: initial;
324
+ --oc-motion-timing-disclosure-exit: initial;
325
+ --oc-motion-timing-expand: initial;
326
+ --oc-motion-timing-expand-exit: initial;
315
327
  --oc-motion-timing-feedback: initial;
316
328
  --oc-motion-timing-gesture: initial;
317
- --oc-motion-timing-gesture-settle: initial;
329
+ --oc-motion-timing-gesture-release: initial;
318
330
  --oc-motion-timing-move: initial;
319
331
  --oc-motion-timing-reject: initial;
320
- --oc-motion-timing-reveal: initial;
321
- --oc-motion-timing-reveal-exit: initial;
332
+ --oc-motion-timing-sheet: initial;
333
+ --oc-motion-timing-sheet-exit: initial;
334
+ --oc-motion-timing-sheet-release: initial;
335
+ --oc-motion-timing-surface: initial;
336
+ --oc-motion-timing-surface-exit: initial;
322
337
  --oc-space-item-gap-huge: initial;
323
338
  --oc-space-item-gap-large: initial;
324
339
  --oc-space-item-gap-medium: initial;
@@ -373,7 +388,6 @@
373
388
  --oc-typo-height-title1: initial;
374
389
  --oc-typo-height-title2: initial;
375
390
  --oc-typo-height-title3: initial;
376
- --oc-typo-scale: initial;
377
391
  --oc-typo-size-body1: initial;
378
392
  --oc-typo-size-body1reading: initial;
379
393
  --oc-typo-size-body2: initial;
@@ -382,6 +396,7 @@
382
396
  --oc-typo-size-display1: initial;
383
397
  --oc-typo-size-display2: initial;
384
398
  --oc-typo-size-display3: initial;
399
+ --oc-typo-size-factor: initial;
385
400
  --oc-typo-size-heading1: initial;
386
401
  --oc-typo-size-heading2: initial;
387
402
  --oc-typo-size-headline1: initial;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orioncactuscorp/ui",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "orioncactus UI foundations and core primitives",
5
5
  "private": false,
6
6
  "type": "module",
@@ -119,7 +119,7 @@
119
119
  "vitest": "^3.2.4"
120
120
  },
121
121
  "dependencies": {
122
- "@orioncactuscorp/icons": "^1.0.0",
122
+ "@orioncactuscorp/icons": "^1.3.0",
123
123
  "@base-ui-components/react": "^1.0.0-rc.0"
124
124
  }
125
125
  }
@@ -1,18 +1,20 @@
1
1
  @use '../mixins/breakpoint' as *;
2
2
  @use '../mixins/color' as *;
3
+ @use '../mixins/radius' as *;
3
4
  @use '../mixins/utils' as *;
4
5
 
5
6
  :root {
6
- --oc-atomic-radius-max: 10000px;
7
- --oc-atomic-radius-huge: #{fluidClamp(toRem(24), $breakpoint-md, from, toRem(64))};
8
- --oc-atomic-radius-xlarge: #{fluidClamp(toRem(12), $breakpoint-md, from, toRem(32))};
9
- --oc-atomic-radius-large: #{fluidClamp(toRem(12), $breakpoint-md, from, toRem(24))};
10
- --oc-atomic-radius-medium: #{fluidClamp(toRem(12), $breakpoint-md, from, toRem(16))};
11
- --oc-atomic-radius-small: #{fluidClamp(toRem(10), $breakpoint-md, from, toRem(12))};
12
- --oc-atomic-radius-xsmall: #{fluidClamp(toRem(8), $breakpoint-md, from, toRem(10))};
13
- --oc-atomic-radius-mini: #{fluidClamp(toRem(6), $breakpoint-md, from, toRem(8))};
14
- --oc-atomic-radius-tiny: #{fluidClamp(toRem(4), $breakpoint-md, from, toRem(6))};
15
- --oc-atomic-radius-micro: #{toRem(4)};
7
+ --oc-atomic-radius-factor: 1;
8
+ --oc-atomic-radius-max: #{oc-radius(10000px)};
9
+ --oc-atomic-radius-huge: #{oc-radius(fluidClamp(toRem(24), $breakpoint-md, from, toRem(64)))};
10
+ --oc-atomic-radius-xlarge: #{oc-radius(fluidClamp(toRem(12), $breakpoint-md, from, toRem(32)))};
11
+ --oc-atomic-radius-large: #{oc-radius(fluidClamp(toRem(12), $breakpoint-md, from, toRem(24)))};
12
+ --oc-atomic-radius-medium: #{oc-radius(fluidClamp(toRem(12), $breakpoint-md, from, toRem(16)))};
13
+ --oc-atomic-radius-small: #{oc-radius(fluidClamp(toRem(10), $breakpoint-md, from, toRem(12)))};
14
+ --oc-atomic-radius-xsmall: #{oc-radius(fluidClamp(toRem(8), $breakpoint-md, from, toRem(10)))};
15
+ --oc-atomic-radius-mini: #{oc-radius(fluidClamp(toRem(6), $breakpoint-md, from, toRem(8)))};
16
+ --oc-atomic-radius-tiny: #{oc-radius(fluidClamp(toRem(4), $breakpoint-md, from, toRem(6)))};
17
+ --oc-atomic-radius-micro: #{oc-radius(toRem(4))};
16
18
 
17
19
  --oc-atomic-width-content: 35em;
18
20
  --oc-atomic-width-section-large: 60rem;
@@ -0,0 +1,34 @@
1
+ import { compileString } from 'sass';
2
+ import { describe, expect, it } from 'vitest';
3
+
4
+ describe('atomic foundation', () => {
5
+ const compileAtomic = () =>
6
+ compileString("@use 'foundations/atomic' as *;", {
7
+ loadPaths: ['src/scss'],
8
+ });
9
+
10
+ it('exposes a global atomic radius factor', () => {
11
+ const result = compileAtomic();
12
+
13
+ expect(result.css).toContain('--oc-atomic-radius-factor: 1;');
14
+ });
15
+
16
+ it('applies the radius factor to max and fixed radius tokens', () => {
17
+ const result = compileAtomic();
18
+
19
+ expect(result.css).toContain(
20
+ '--oc-atomic-radius-max: max(0px, 10000px * var(--oc-atomic-radius-factor));',
21
+ );
22
+ expect(result.css).toContain(
23
+ '--oc-atomic-radius-micro: max(0px, 0.25rem * var(--oc-atomic-radius-factor));',
24
+ );
25
+ });
26
+
27
+ it('applies the radius factor to fluid radius tokens', () => {
28
+ const result = compileAtomic();
29
+
30
+ expect(result.css).toContain(
31
+ '--oc-atomic-radius-medium: max(0px, clamp(0.75rem, 12 / 768 * 100vw, 1rem) * var(--oc-atomic-radius-factor));',
32
+ );
33
+ });
34
+ });
@@ -1,15 +1,15 @@
1
1
  @use '../spring' as *;
2
2
 
3
3
  :root {
4
- --oc-motion-duration-scale: 1;
5
- --oc-motion-duration-scale-reduced: 0.75;
6
- --oc-motion-duration-scale-preserve: 1;
7
- --oc-motion-scale: 1;
8
- --oc-motion-scale-feedback: var(--oc-motion-scale);
9
- --oc-motion-scale-reveal: var(--oc-motion-scale);
10
- --oc-motion-distance-scale: 1;
11
- --oc-motion-distance-scale-reject: var(--oc-motion-distance-scale);
12
- --oc-motion-distance-scale-reveal: var(--oc-motion-distance-scale);
4
+ --oc-motion-duration-factor: 1;
5
+ --oc-motion-duration-factor-reduced: 0.75;
6
+ --oc-motion-duration-factor-preserve: 1;
7
+ --oc-motion-scale-factor: 1;
8
+ --oc-motion-scale-factor-feedback: var(--oc-motion-scale-factor);
9
+ --oc-motion-scale-factor-spatial: var(--oc-motion-scale-factor);
10
+ --oc-motion-distance-factor: 1;
11
+ --oc-motion-distance-factor-reject: var(--oc-motion-distance-factor);
12
+ --oc-motion-distance-factor-spatial: var(--oc-motion-distance-factor);
13
13
 
14
14
  /* Pace tokens are unitless duration multipliers. */
15
15
  --oc-motion-pace-quick: 0.75;
@@ -17,19 +17,33 @@
17
17
  --oc-motion-pace-slow: 1.35;
18
18
  --oc-motion-duration-feedback: 0.2s;
19
19
  --oc-motion-duration-reject: #{oc-spring-settle-duration(0.32s, smooth)};
20
- --oc-motion-duration-reveal: 0.3s;
21
- --oc-motion-duration-reveal-exit: 0.125s;
20
+ --oc-motion-duration-disclosure: 0.2s;
21
+ --oc-motion-duration-disclosure-exit: 0.14s;
22
+ --oc-motion-duration-surface: 0.26s;
23
+ --oc-motion-duration-surface-exit: 0.24s;
24
+ --oc-motion-duration-sheet: 0.26s;
25
+ --oc-motion-duration-sheet-exit: 0.33s;
26
+ --oc-motion-duration-sheet-release: 0.33s;
27
+ --oc-motion-duration-expand: 0.3s;
28
+ --oc-motion-duration-expand-exit: 0.24s;
22
29
  --oc-motion-duration-move: 0.3s;
23
30
  --oc-motion-duration-gesture: 0.2s;
24
- --oc-motion-duration-gesture-settle: 0.52s;
31
+ --oc-motion-duration-gesture-release: 0.52s;
25
32
  --oc-motion-duration-ambient: 1.2s;
26
33
  --oc-motion-timing-feedback: ease;
27
34
  --oc-motion-timing-reject: #{oc-spring-timing(0.32s, smooth, $samples: 64)};
28
- --oc-motion-timing-reveal: ease;
29
- --oc-motion-timing-reveal-exit: ease-in;
35
+ --oc-motion-timing-disclosure: ease;
36
+ --oc-motion-timing-disclosure-exit: cubic-bezier(0.8, 0, 1, 1);
37
+ --oc-motion-timing-surface: ease;
38
+ --oc-motion-timing-surface-exit: ease-in;
39
+ --oc-motion-timing-sheet: ease;
40
+ --oc-motion-timing-sheet-exit: ease-in;
41
+ --oc-motion-timing-sheet-release: ease;
42
+ --oc-motion-timing-expand: ease;
43
+ --oc-motion-timing-expand-exit: ease-in;
30
44
  --oc-motion-timing-move: cubic-bezier(0.32, 0.72, 0, 1);
31
45
  --oc-motion-timing-gesture: ease;
32
- --oc-motion-timing-gesture-settle: cubic-bezier(0.32, 0.72, 0, 1);
46
+ --oc-motion-timing-gesture-release: cubic-bezier(0.32, 0.72, 0, 1);
33
47
  --oc-motion-timing-ambient: linear;
34
48
  --oc-spring-smooth-timing: #{oc-spring-timing(0.5s, smooth, $samples: 64)};
35
49
  --oc-spring-smooth-duration-scale: #{oc-spring-duration-scale(0.5s, smooth)};
@@ -41,6 +55,6 @@
41
55
 
42
56
  @media (prefers-reduced-motion: reduce) {
43
57
  :root {
44
- --oc-motion-duration-scale: var(--oc-motion-duration-scale-reduced);
58
+ --oc-motion-duration-factor: var(--oc-motion-duration-factor-reduced);
45
59
  }
46
60
  }
@@ -364,7 +364,7 @@ $oc-typo-responsive-segments: (
364
364
  */
365
365
  :root {
366
366
  --oc-typo-font-family: #{$oc-typo-font-family};
367
- --oc-typo-scale: 1;
367
+ --oc-typo-size-factor: 1;
368
368
 
369
369
  @include oc-emit-responsive-tokens('typo-size', $oc-typo-size-stops, $oc-typo-responsive-segments, $oc-responsive-typo-stops, '$oc-responsive-typo-stops');
370
370
  @include oc-emit-responsive-tokens('typo-height', $oc-typo-height-stops, $oc-typo-responsive-segments, $oc-responsive-typo-stops, '$oc-responsive-typo-stops');
@@ -14,5 +14,6 @@
14
14
  @forward './mixins/color';
15
15
  @forward './mixins/typo';
16
16
  @forward './mixins/element';
17
+ @forward './mixins/radius';
17
18
  @forward './mixins/motion';
18
19
  @forward './spring';
@@ -1,3 +1,6 @@
1
+ @use 'sass:list';
2
+ @use 'sass:math';
3
+ @use 'sass:meta';
1
4
  @use './motion' as *;
2
5
 
3
6
  @mixin element-button {
@@ -25,28 +28,85 @@
25
28
  user-select: none;
26
29
  }
27
30
 
28
- @mixin inset-stroke($width, $color: currentcolor) {
31
+ @function _oc-border-is-half-px($width) {
32
+ @if meta.type-of($width) != 'number' or math.unit($width) != 'px' {
33
+ @return false;
34
+ }
35
+
36
+ $pixels: math.div($width, 1px);
37
+
38
+ @return $pixels == math.floor($pixels) + 0.5;
39
+ }
40
+
41
+ @function _oc-border-webkit-base-spread($width) {
42
+ $pixels: math.floor(math.div($width, 1px));
43
+
44
+ @if $pixels < 1 {
45
+ @return 1px;
46
+ }
47
+
48
+ @return $pixels * 1px;
49
+ }
50
+
51
+ @function _oc-border-webkit-device-spread($width, $dppx) {
52
+ $pixels: math.div($width, 1px);
53
+ $device-pixels: math.ceil($pixels * $dppx);
54
+ $spread: math.div($device-pixels, $dppx);
55
+ $precision: math.pow(10, 4);
56
+
57
+ @return math.div(math.floor($spread * $precision), $precision) * 1px;
58
+ }
59
+
60
+ @function _oc-border-args($args) {
61
+ $count: list.length($args);
62
+
63
+ @if $count == 0 {
64
+ @return (1px, currentColor);
65
+ }
66
+
67
+ @if $count == 1 {
68
+ $value: list.nth($args, 1);
69
+
70
+ @if meta.type-of($value) == 'number' {
71
+ @return ($value, currentColor);
72
+ }
73
+
74
+ @return (1px, $value);
75
+ }
76
+
77
+ @if $count == 2 {
78
+ @return (list.nth($args, 1), list.nth($args, 2));
79
+ }
80
+
81
+ @error 'oc-border accepts zero, one, or two arguments.';
82
+ }
83
+
84
+ @mixin oc-border($args...) {
85
+ $resolved: _oc-border-args($args);
86
+ $width: list.nth($resolved, 1);
87
+ $color: list.nth($resolved, 2);
88
+
29
89
  box-shadow: inset 0 0 0 $width $color;
30
90
 
31
- @if $width == 1.5px {
32
- /* stylelint-disable block-no-redundant-nested-style-rules -- WebKit rounds 1.5px inset strokes differently by DPR; nested selectors keep the correction scoped. */
91
+ @if _oc-border-is-half-px($width) {
92
+ /* stylelint-disable block-no-redundant-nested-style-rules, media-feature-range-notation -- WebKit rounds fractional inset strokes differently by DPR; nested selectors keep the correction scoped. */
33
93
  @supports (-webkit-backdrop-filter: none) {
34
94
  & {
35
- box-shadow: inset 0 0 0 1px $color;
95
+ box-shadow: inset 0 0 0 _oc-border-webkit-base-spread($width) $color;
36
96
  }
37
97
 
38
98
  @media only screen and (resolution: 2dppx) {
39
99
  & {
40
- box-shadow: inset 0 0 0 1.5px $color;
100
+ box-shadow: inset 0 0 0 _oc-border-webkit-device-spread($width, 2) $color;
41
101
  }
42
102
  }
43
103
 
44
104
  @media only screen and (resolution: 3dppx) {
45
105
  & {
46
- box-shadow: inset 0 0 0 1.6666px $color;
106
+ box-shadow: inset 0 0 0 _oc-border-webkit-device-spread($width, 3) $color;
47
107
  }
48
108
  }
49
109
  }
50
- /* stylelint-enable block-no-redundant-nested-style-rules */
110
+ /* stylelint-enable block-no-redundant-nested-style-rules, media-feature-range-notation */
51
111
  }
52
112
  }
@@ -1,14 +1,48 @@
1
1
  import { compileString } from 'sass';
2
2
  import { describe, expect, it } from 'vitest';
3
3
 
4
- describe('inset-stroke mixin', () => {
5
- it('emits a direct inset stroke for integer widths', () => {
4
+ describe('oc-border mixin', () => {
5
+ it('emits a 1px currentColor inset border by default', () => {
6
6
  const result = compileString(
7
7
  `
8
8
  @use 'element' as *;
9
9
 
10
10
  .target {
11
- @include inset-stroke(2px, var(--stroke));
11
+ @include oc-border;
12
+ }
13
+ `,
14
+ {
15
+ loadPaths: ['src/scss/mixins'],
16
+ },
17
+ );
18
+
19
+ expect(result.css).toContain('box-shadow: inset 0 0 0 1px currentColor;');
20
+ });
21
+
22
+ it('treats a single non-number argument as the border color', () => {
23
+ const result = compileString(
24
+ `
25
+ @use 'element' as *;
26
+
27
+ .target {
28
+ @include oc-border(black);
29
+ }
30
+ `,
31
+ {
32
+ loadPaths: ['src/scss/mixins'],
33
+ },
34
+ );
35
+
36
+ expect(result.css).toContain('box-shadow: inset 0 0 0 1px black;');
37
+ });
38
+
39
+ it('emits a direct inset border for integer widths', () => {
40
+ const result = compileString(
41
+ `
42
+ @use 'element' as *;
43
+
44
+ .target {
45
+ @include oc-border(2px, var(--stroke));
12
46
  }
13
47
  `,
14
48
  {
@@ -20,13 +54,13 @@ describe('inset-stroke mixin', () => {
20
54
  expect(result.css).not.toContain('@supports');
21
55
  });
22
56
 
23
- it('adds WebKit DPR correction for 1.5px strokes', () => {
57
+ it('adds WebKit DPR correction for 1.5px borders', () => {
24
58
  const result = compileString(
25
59
  `
26
60
  @use 'element' as *;
27
61
 
28
62
  .target {
29
- @include inset-stroke(1.5px);
63
+ @include oc-border(1.5px);
30
64
  }
31
65
  `,
32
66
  {
@@ -34,11 +68,64 @@ describe('inset-stroke mixin', () => {
34
68
  },
35
69
  );
36
70
 
37
- expect(result.css).toContain('box-shadow: inset 0 0 0 1.5px currentcolor;');
71
+ expect(result.css).toContain('box-shadow: inset 0 0 0 1.5px currentColor;');
38
72
  expect(result.css).toContain('@supports (-webkit-backdrop-filter: none)');
73
+ expect(result.css).toContain('box-shadow: inset 0 0 0 1px currentColor;');
74
+ expect(result.css).toContain('@media only screen and (resolution: 2dppx)');
39
75
  expect(result.css).toContain('@media only screen and (resolution: 3dppx)');
40
76
  expect(result.css).toContain(
41
- 'box-shadow: inset 0 0 0 1.6666px currentcolor;',
77
+ 'box-shadow: inset 0 0 0 1.6666px currentColor;',
42
78
  );
43
79
  });
80
+
81
+ it('generalizes WebKit DPR correction for half-pixel borders', () => {
82
+ const result = compileString(
83
+ `
84
+ @use 'element' as *;
85
+
86
+ .two {
87
+ @include oc-border(2.5px, var(--stroke));
88
+ }
89
+
90
+ .three {
91
+ @include oc-border(3.5px, var(--stroke));
92
+ }
93
+ `,
94
+ {
95
+ loadPaths: ['src/scss/mixins'],
96
+ },
97
+ );
98
+
99
+ expect(result.css).toContain('box-shadow: inset 0 0 0 2px var(--stroke);');
100
+ expect(result.css).toContain(
101
+ 'box-shadow: inset 0 0 0 2.5px var(--stroke);',
102
+ );
103
+ expect(result.css).toContain(
104
+ 'box-shadow: inset 0 0 0 2.6666px var(--stroke);',
105
+ );
106
+ expect(result.css).toContain('box-shadow: inset 0 0 0 3px var(--stroke);');
107
+ expect(result.css).toContain(
108
+ 'box-shadow: inset 0 0 0 3.5px var(--stroke);',
109
+ );
110
+ expect(result.css).toContain(
111
+ 'box-shadow: inset 0 0 0 3.6666px var(--stroke);',
112
+ );
113
+ });
114
+
115
+ it('does not expose the removed inset-stroke mixin', () => {
116
+ expect(() =>
117
+ compileString(
118
+ `
119
+ @use 'element' as *;
120
+
121
+ .target {
122
+ @include inset-stroke(1px);
123
+ }
124
+ `,
125
+ {
126
+ loadPaths: ['src/scss/mixins'],
127
+ },
128
+ ),
129
+ ).toThrow('Undefined mixin');
130
+ });
44
131
  });