@orioncactuscorp/ui 1.14.0 → 1.15.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.
- package/CHANGELOG.md +92 -0
- package/README.md +65 -36
- package/dist/components/BackgroundIconButton/BackgroundIconButton.css +1 -1
- package/dist/components/Button/Button.css +1 -1
- package/dist/components/CheckMark/CheckMark.js +19 -17
- package/dist/components/Checkbox/Checkbox.css +1 -1
- package/dist/components/Checkbox/Checkbox.js +38 -36
- package/dist/components/FloatingWindow/FloatingWindow.js +138 -137
- package/dist/components/Interaction/Interaction.css +1 -1
- package/dist/components/Menu/Menu.css +1 -1
- package/dist/components/Menu/Menu.js +95 -87
- package/dist/components/Modal/Modal.css +1 -1
- package/dist/components/Modal/Modal.js +504 -488
- package/dist/components/Modal/Modal.module.scss.js +69 -62
- package/dist/components/Modal/keyboardSession.js +104 -0
- package/dist/components/Modal/snapPoints.js +95 -82
- package/dist/components/Modal/useModalDrag.js +587 -637
- package/dist/components/Modal/useModalKeyboardAvoidance.js +159 -0
- package/dist/components/Modal/useModalViewportGeometry.js +36 -0
- package/dist/components/PositionSnap/PositionSnap.css +1 -0
- package/dist/components/PositionSnap/PositionSnap.js +330 -0
- package/dist/components/PositionSnap/PositionSnap.module.scss.js +9 -0
- package/dist/components/PositionSnap/contexts.js +12 -0
- package/dist/components/PositionSnap/geometry.js +75 -0
- package/dist/components/PositionSnap/gesture.js +7 -0
- package/dist/components/PositionSnap/policy.js +60 -0
- package/dist/components/PositionSnap.d.ts +6 -0
- package/dist/components/PositionSnap.js +7 -0
- package/dist/components/ProgressIndicator/ProgressIndicator.css +1 -0
- package/dist/components/ProgressIndicator/ProgressIndicator.js +45 -0
- package/dist/components/ProgressIndicator/ProgressIndicator.module.scss.js +13 -0
- package/dist/components/ProgressIndicator.d.ts +6 -0
- package/dist/components/ProgressIndicator.js +4 -0
- package/dist/components/Radio/Radio.css +1 -1
- package/dist/components/Radio/Radio.js +26 -24
- package/dist/components/Switch/Switch.css +1 -1
- package/dist/components/Switch/Switch.js +18 -16
- package/dist/components/Tabs/Tabs.css +1 -1
- package/dist/components/Tooltip/Tooltip.css +1 -1
- package/dist/components/Tooltip/Tooltip.js +59 -56
- package/dist/components/Tooltip/Tooltip.module.scss.js +6 -4
- package/dist/components/internal/ModalPresentation.js +126 -124
- package/dist/components/internal/OverlayPortalContext.js +20 -0
- package/dist/index.js +133 -126
- package/dist/styles.css +12 -10
- package/dist/ui/src/components/CheckMark/CheckMark.d.ts +1 -1
- package/dist/ui/src/components/CheckMark/CheckMark.d.ts.map +1 -1
- package/dist/ui/src/components/CheckMark/types.d.ts +2 -1
- package/dist/ui/src/components/CheckMark/types.d.ts.map +1 -1
- package/dist/ui/src/components/Checkbox/Checkbox.d.ts +1 -1
- package/dist/ui/src/components/Checkbox/Checkbox.d.ts.map +1 -1
- package/dist/ui/src/components/Checkbox/types.d.ts +2 -1
- package/dist/ui/src/components/Checkbox/types.d.ts.map +1 -1
- package/dist/ui/src/components/FloatingWindow/FloatingWindow.d.ts.map +1 -1
- package/dist/ui/src/components/FloatingWindow/types.d.ts +1 -1
- package/dist/ui/src/components/FloatingWindow/types.d.ts.map +1 -1
- package/dist/ui/src/components/Interaction/index.d.ts +1 -1
- package/dist/ui/src/components/Interaction/index.d.ts.map +1 -1
- package/dist/ui/src/components/Interaction/types.d.ts +9 -1
- package/dist/ui/src/components/Interaction/types.d.ts.map +1 -1
- package/dist/ui/src/components/Menu/Menu.d.ts +2 -2
- package/dist/ui/src/components/Menu/Menu.d.ts.map +1 -1
- package/dist/ui/src/components/Menu/types.d.ts +9 -2
- package/dist/ui/src/components/Menu/types.d.ts.map +1 -1
- package/dist/ui/src/components/Modal/Modal.d.ts +1 -1
- package/dist/ui/src/components/Modal/Modal.d.ts.map +1 -1
- package/dist/ui/src/components/Modal/keyboardSession.d.ts +46 -0
- package/dist/ui/src/components/Modal/keyboardSession.d.ts.map +1 -0
- package/dist/ui/src/components/Modal/snapPoints.d.ts.map +1 -1
- package/dist/ui/src/components/Modal/types.d.ts +8 -1
- package/dist/ui/src/components/Modal/types.d.ts.map +1 -1
- package/dist/ui/src/components/Modal/useModalDrag.d.ts.map +1 -1
- package/dist/ui/src/components/Modal/useModalKeyboardAvoidance.d.ts +17 -0
- package/dist/ui/src/components/Modal/useModalKeyboardAvoidance.d.ts.map +1 -0
- package/dist/ui/src/components/Modal/useModalViewportGeometry.d.ts +9 -0
- package/dist/ui/src/components/Modal/useModalViewportGeometry.d.ts.map +1 -0
- package/dist/ui/src/components/PositionSnap/PositionSnap.d.ts +11 -0
- package/dist/ui/src/components/PositionSnap/PositionSnap.d.ts.map +1 -0
- package/dist/ui/src/components/PositionSnap/contexts.d.ts +17 -0
- package/dist/ui/src/components/PositionSnap/contexts.d.ts.map +1 -0
- package/dist/ui/src/components/PositionSnap/geometry.d.ts +31 -0
- package/dist/ui/src/components/PositionSnap/geometry.d.ts.map +1 -0
- package/dist/ui/src/components/PositionSnap/gesture.d.ts +2 -0
- package/dist/ui/src/components/PositionSnap/gesture.d.ts.map +1 -0
- package/dist/ui/src/components/PositionSnap/index.d.ts +3 -0
- package/dist/ui/src/components/PositionSnap/index.d.ts.map +1 -0
- package/dist/ui/src/components/PositionSnap/policy.d.ts +24 -0
- package/dist/ui/src/components/PositionSnap/policy.d.ts.map +1 -0
- package/dist/ui/src/components/PositionSnap/types.d.ts +31 -0
- package/dist/ui/src/components/PositionSnap/types.d.ts.map +1 -0
- package/dist/ui/src/components/ProgressIndicator/ProgressIndicator.d.ts +4 -0
- package/dist/ui/src/components/ProgressIndicator/ProgressIndicator.d.ts.map +1 -0
- package/dist/ui/src/components/ProgressIndicator/index.d.ts +3 -0
- package/dist/ui/src/components/ProgressIndicator/index.d.ts.map +1 -0
- package/dist/ui/src/components/ProgressIndicator/types.d.ts +17 -0
- package/dist/ui/src/components/ProgressIndicator/types.d.ts.map +1 -0
- package/dist/ui/src/components/Radio/Radio.d.ts +1 -1
- package/dist/ui/src/components/Radio/Radio.d.ts.map +1 -1
- package/dist/ui/src/components/Radio/types.d.ts +2 -1
- package/dist/ui/src/components/Radio/types.d.ts.map +1 -1
- package/dist/ui/src/components/Switch/Switch.d.ts +1 -1
- package/dist/ui/src/components/Switch/Switch.d.ts.map +1 -1
- package/dist/ui/src/components/Switch/types.d.ts +2 -1
- package/dist/ui/src/components/Switch/types.d.ts.map +1 -1
- package/dist/ui/src/components/Tooltip/Tooltip.d.ts +1 -1
- package/dist/ui/src/components/Tooltip/Tooltip.d.ts.map +1 -1
- package/dist/ui/src/components/Tooltip/types.d.ts +6 -0
- package/dist/ui/src/components/Tooltip/types.d.ts.map +1 -1
- package/dist/ui/src/components/internal/ModalPresentation.d.ts +2 -1
- package/dist/ui/src/components/internal/ModalPresentation.d.ts.map +1 -1
- package/dist/ui/src/components/internal/OverlayPortalContext.d.ts +10 -0
- package/dist/ui/src/components/internal/OverlayPortalContext.d.ts.map +1 -0
- package/dist/ui/src/index.d.ts +5 -1
- package/dist/ui/src/index.d.ts.map +1 -1
- package/docs/api-conventions.md +4 -0
- package/docs/overlay-stacking.md +48 -0
- package/docs/responsive-foundation-profile.md +1 -1
- package/docs/scss-helpers.md +110 -5
- package/docs/selector-contract.md +77 -2
- package/docs/tokens.md +2 -0
- package/package.json +1 -1
- package/src/scss/foundations/atomic.scss +9 -8
- package/src/scss/index.scss +2 -0
- package/src/scss/mixins/_fluid.scss +57 -0
- package/src/scss/mixins/_fluid.test.ts +87 -0
- package/src/scss/mixins/_gradient.scss +99 -0
- package/src/scss/mixins/_gradient.test.ts +111 -0
- package/src/scss/mixins/_responsive.scss +2 -1
- package/src/scss/mixins/_typo.scss +2 -1
- package/src/scss/mixins/_units.scss +14 -0
- package/src/scss/mixins/_utils.scss +3 -22
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
@use '../mixins/breakpoint' as *;
|
|
2
2
|
@use '../mixins/color' as *;
|
|
3
|
+
@use '../mixins/fluid' as *;
|
|
3
4
|
@use '../mixins/radius' as *;
|
|
4
5
|
@use '../mixins/utils' as *;
|
|
5
6
|
|
|
6
7
|
:root {
|
|
7
8
|
--oc-atomic-radius-factor: 1;
|
|
8
9
|
--oc-atomic-radius-max: #{oc-radius(10000px)};
|
|
9
|
-
--oc-atomic-radius-huge: #{oc-radius(
|
|
10
|
-
--oc-atomic-radius-xlarge: #{oc-radius(
|
|
11
|
-
--oc-atomic-radius-large: #{oc-radius(
|
|
12
|
-
--oc-atomic-radius-medium: #{oc-radius(
|
|
13
|
-
--oc-atomic-radius-small: #{oc-radius(
|
|
14
|
-
--oc-atomic-radius-xsmall: #{oc-radius(
|
|
15
|
-
--oc-atomic-radius-mini: #{oc-radius(
|
|
16
|
-
--oc-atomic-radius-tiny: #{oc-radius(
|
|
10
|
+
--oc-atomic-radius-huge: #{oc-radius(oc-fluid(toRem(24), toRem(64), $breakpoint-md, from))};
|
|
11
|
+
--oc-atomic-radius-xlarge: #{oc-radius(oc-fluid(toRem(12), toRem(32), $breakpoint-md, from))};
|
|
12
|
+
--oc-atomic-radius-large: #{oc-radius(oc-fluid(toRem(12), toRem(24), $breakpoint-md, from))};
|
|
13
|
+
--oc-atomic-radius-medium: #{oc-radius(oc-fluid(toRem(12), toRem(16), $breakpoint-md, from))};
|
|
14
|
+
--oc-atomic-radius-small: #{oc-radius(oc-fluid(toRem(10), toRem(12), $breakpoint-md, from))};
|
|
15
|
+
--oc-atomic-radius-xsmall: #{oc-radius(oc-fluid(toRem(8), toRem(10), $breakpoint-md, from))};
|
|
16
|
+
--oc-atomic-radius-mini: #{oc-radius(oc-fluid(toRem(6), toRem(8), $breakpoint-md, from))};
|
|
17
|
+
--oc-atomic-radius-tiny: #{oc-radius(oc-fluid(toRem(4), toRem(6), $breakpoint-md, from))};
|
|
17
18
|
--oc-atomic-radius-micro: #{oc-radius(toRem(4))};
|
|
18
19
|
|
|
19
20
|
--oc-atomic-width-content: 35em;
|
package/src/scss/index.scss
CHANGED
|
@@ -11,8 +11,10 @@
|
|
|
11
11
|
@forward './foundations/reset';
|
|
12
12
|
@forward './mixins/breakpoint';
|
|
13
13
|
@forward './mixins/utils';
|
|
14
|
+
@forward './mixins/fluid';
|
|
14
15
|
@forward './mixins/responsive';
|
|
15
16
|
@forward './mixins/color';
|
|
17
|
+
@forward './mixins/gradient';
|
|
16
18
|
@forward './mixins/typo';
|
|
17
19
|
@forward './mixins/element';
|
|
18
20
|
@forward './mixins/focus';
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
@use 'sass:math';
|
|
2
|
+
@use 'sass:meta';
|
|
3
|
+
@use './units' as *;
|
|
4
|
+
|
|
5
|
+
@function _oc-fluid-px-value($value) {
|
|
6
|
+
@if meta.type-of($value) != 'number' {
|
|
7
|
+
@error 'oc-fluid() min and max values must be Sass numbers with px or rem units.';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
@if math.is-unitless($value) {
|
|
11
|
+
@error 'oc-fluid() min and max values must use px or rem units.';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@if math.unit($value) == 'px' {
|
|
15
|
+
@return stripUnit($value);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@if math.unit($value) == 'rem' {
|
|
19
|
+
@return stripUnit($value) * $rem-unit;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@error 'oc-fluid() min and max values must use px or rem units.';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@function _oc-fluid-viewport-value($bp) {
|
|
26
|
+
@if meta.type-of($bp) != 'number' {
|
|
27
|
+
@error 'oc-fluid() $bp must be a Sass number in px or unitless form.';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@if not math.is-unitless($bp) and math.unit($bp) != 'px' {
|
|
31
|
+
@error 'oc-fluid() $bp must be a Sass number in px or unitless form.';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
$bp-num: stripUnit($bp);
|
|
35
|
+
|
|
36
|
+
@if $bp-num <= 0 {
|
|
37
|
+
@error 'oc-fluid() $bp must be greater than zero.';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@return $bp-num;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@function oc-fluid($min, $max, $bp, $anchor: to) {
|
|
44
|
+
$min-px: _oc-fluid-px-value($min);
|
|
45
|
+
$max-px: _oc-fluid-px-value($max);
|
|
46
|
+
$basis-px: $max-px;
|
|
47
|
+
|
|
48
|
+
@if $anchor == from {
|
|
49
|
+
$basis-px: $min-px;
|
|
50
|
+
} @else if $anchor != to {
|
|
51
|
+
@error "oc-fluid() anchor must be 'from' or 'to'.";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
$bp-num: _oc-fluid-viewport-value($bp);
|
|
55
|
+
|
|
56
|
+
@return clamp(#{$min}, calc(#{$basis-px} / #{$bp-num} * 100vw), #{$max});
|
|
57
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { compileString } from 'sass';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
|
|
4
|
+
const compileScss = (source: string) =>
|
|
5
|
+
compileString(source, {
|
|
6
|
+
loadPaths: ['src/scss/mixins'],
|
|
7
|
+
style: 'compressed',
|
|
8
|
+
}).css.replace(/:\s+/g, ':');
|
|
9
|
+
|
|
10
|
+
describe('oc-fluid function', () => {
|
|
11
|
+
it('defaults to the to anchor and accepts a direct pixel viewport basis', () => {
|
|
12
|
+
const css = compileScss(`
|
|
13
|
+
@use 'fluid' as *;
|
|
14
|
+
@use 'units' as *;
|
|
15
|
+
|
|
16
|
+
.title {
|
|
17
|
+
font-size: oc-fluid(toRem(16), toRem(20), 375px);
|
|
18
|
+
}
|
|
19
|
+
`);
|
|
20
|
+
|
|
21
|
+
expect(css).toContain('.title{font-size:clamp(1rem,20/375*100vw,1.25rem)}');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('accepts a named breakpoint value and the from anchor', () => {
|
|
25
|
+
const css = compileScss(`
|
|
26
|
+
@use 'breakpoint' as *;
|
|
27
|
+
@use 'fluid' as *;
|
|
28
|
+
@use 'units' as units;
|
|
29
|
+
|
|
30
|
+
.radius {
|
|
31
|
+
border-radius: oc-fluid(units.toRem(10), units.toRem(12), $breakpoint-md, from);
|
|
32
|
+
}
|
|
33
|
+
`);
|
|
34
|
+
|
|
35
|
+
expect(css).toContain(
|
|
36
|
+
'.radius{border-radius:clamp(0.625rem,10/768*100vw,0.75rem)}',
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('supports pixel endpoints and mixed compatible length units', () => {
|
|
41
|
+
const css = compileScss(`
|
|
42
|
+
@use 'fluid' as *;
|
|
43
|
+
|
|
44
|
+
.pixel {
|
|
45
|
+
font-size: oc-fluid(16px, 20px, 375px);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.mixed {
|
|
49
|
+
padding-block: oc-fluid(1rem, 20px, 375px);
|
|
50
|
+
}
|
|
51
|
+
`);
|
|
52
|
+
|
|
53
|
+
expect(css).toContain('.pixel{font-size:clamp(16px,20/375*100vw,20px)}');
|
|
54
|
+
expect(css).toContain(
|
|
55
|
+
'.mixed{padding-block:clamp(1rem,20/375*100vw,20px)}',
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('rejects an unknown anchor', () => {
|
|
60
|
+
expect(() =>
|
|
61
|
+
compileScss(`
|
|
62
|
+
@use 'fluid' as *;
|
|
63
|
+
@use 'units' as *;
|
|
64
|
+
|
|
65
|
+
.invalid {
|
|
66
|
+
font-size: oc-fluid(toRem(16), toRem(20), 375px, center);
|
|
67
|
+
}
|
|
68
|
+
`),
|
|
69
|
+
).toThrow("oc-fluid() anchor must be 'from' or 'to'.");
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('fluidClamp compatibility function', () => {
|
|
74
|
+
it('preserves the legacy argument order', () => {
|
|
75
|
+
const css = compileScss(`
|
|
76
|
+
@use 'utils' as *;
|
|
77
|
+
|
|
78
|
+
.legacy {
|
|
79
|
+
font-size: fluidClamp(toRem(16), 375px, to, toRem(20));
|
|
80
|
+
}
|
|
81
|
+
`);
|
|
82
|
+
|
|
83
|
+
expect(css).toContain(
|
|
84
|
+
'.legacy{font-size:clamp(1rem,20/375*100vw,1.25rem)}',
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
@use 'sass:list';
|
|
2
|
+
@use 'sass:math';
|
|
3
|
+
@use 'sass:meta';
|
|
4
|
+
@use 'sass:string';
|
|
5
|
+
|
|
6
|
+
@function _oc-gradient-require-direction($direction) {
|
|
7
|
+
@if list.index((top, right, bottom, left), $direction) == null {
|
|
8
|
+
@error 'Unknown gradient direction: #{$direction}. Use top, right, bottom, or left.';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
@return $direction;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@function _oc-gradient-position($start, $end, $progress) {
|
|
15
|
+
@if $progress == 0 {
|
|
16
|
+
@return $start;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@if $progress == 1 {
|
|
20
|
+
@return $end;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@return string.unquote('calc(#{$start} + (#{$end} - #{$start}) * #{$progress})');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@function _oc-gradient-color($from, $to, $progress) {
|
|
27
|
+
@if $progress == 0 {
|
|
28
|
+
@return $from;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@if $progress == 1 {
|
|
32
|
+
@return $to;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
$from-weight: math.percentage(1 - $progress);
|
|
36
|
+
|
|
37
|
+
@return string.unquote('color-mix(in srgb, #{$from} #{$from-weight}, #{$to})');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@function _oc-easing-gradient-stops($from, $to, $start, $end) {
|
|
41
|
+
// Eight intervals keep smoothstep boundaries visually soft without making every generated gradient excessively large.
|
|
42
|
+
$stepCount: 8;
|
|
43
|
+
$stops: ();
|
|
44
|
+
|
|
45
|
+
@for $index from 0 through $stepCount {
|
|
46
|
+
$linear-progress: math.div($index, $stepCount);
|
|
47
|
+
|
|
48
|
+
// Smoothstep keeps the color-change velocity at both boundaries continuous.
|
|
49
|
+
$eased-progress: 3 * math.pow($linear-progress, 2) - 2 * math.pow($linear-progress, 3);
|
|
50
|
+
$color: _oc-gradient-color($from, $to, $eased-progress);
|
|
51
|
+
$position: _oc-gradient-position($start, $end, $linear-progress);
|
|
52
|
+
|
|
53
|
+
$stops: list.append($stops, $color $position, comma);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@return $stops;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@function oc-easing-gradient($direction, $from, $to, $start: 0%, $end: 100%) {
|
|
60
|
+
$validated-direction: _oc-gradient-require-direction($direction);
|
|
61
|
+
$stops: _oc-easing-gradient-stops($from, $to, $start, $end);
|
|
62
|
+
|
|
63
|
+
@return linear-gradient(to $validated-direction, $stops);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@function _oc-gradient-mask-image($direction, $start-fade-size, $end-fade-size, $content-size) {
|
|
67
|
+
$validated-direction: _oc-gradient-require-direction($direction);
|
|
68
|
+
$start-stops: _oc-easing-gradient-stops(transparent, black, 0px, $start-fade-size);
|
|
69
|
+
$end-fade-start: string.unquote('calc(#{$content-size} - #{$end-fade-size})');
|
|
70
|
+
$end-stops: _oc-easing-gradient-stops(black, transparent, $end-fade-start, $content-size);
|
|
71
|
+
$stops: list.join($start-stops, $end-stops, comma);
|
|
72
|
+
|
|
73
|
+
@return linear-gradient(to $validated-direction, $stops);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@mixin oc-gradient-mask($direction, $start-fade-size: 0px, $end-fade-size: 0px, $content-size: 100%) {
|
|
77
|
+
$mask: _oc-gradient-mask-image($direction, $start-fade-size, $end-fade-size, $content-size);
|
|
78
|
+
|
|
79
|
+
// stylelint-disable-next-line property-no-vendor-prefix -- Safari still needs the prefixed mask property for gradient fades.
|
|
80
|
+
-webkit-mask-image: $mask;
|
|
81
|
+
// stylelint-disable-next-line declaration-block-no-duplicate-properties -- Keep the standard property after the Safari fallback.
|
|
82
|
+
mask-image: $mask;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@mixin oc-gradient-mask-edges($top-fade-size: 0px, $right-fade-size: 0px, $bottom-fade-size: 0px, $left-fade-size: 0px, $inline-content-size: 100%, $block-content-size: 100%) {
|
|
86
|
+
$block-mask: _oc-gradient-mask-image(bottom, $top-fade-size, $bottom-fade-size, $block-content-size);
|
|
87
|
+
$inline-mask: _oc-gradient-mask-image(right, $left-fade-size, $right-fade-size, $inline-content-size);
|
|
88
|
+
|
|
89
|
+
// Both layers encode only black alpha, so intersection multiplies corner alpha and is order-independent.
|
|
90
|
+
$masks: $block-mask, $inline-mask;
|
|
91
|
+
|
|
92
|
+
// stylelint-disable-next-line property-no-vendor-prefix -- WebKit uses its legacy Porter-Duff keyword for mask intersection.
|
|
93
|
+
-webkit-mask-composite: source-in;
|
|
94
|
+
// stylelint-disable-next-line property-no-vendor-prefix -- Safari still needs the prefixed mask property for gradient fades.
|
|
95
|
+
-webkit-mask-image: $masks;
|
|
96
|
+
mask-composite: intersect;
|
|
97
|
+
// stylelint-disable-next-line declaration-block-no-duplicate-properties -- Keep the standard property after the Safari fallback.
|
|
98
|
+
mask-image: $masks;
|
|
99
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { compileString } from 'sass';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
|
|
4
|
+
function compile(source: string) {
|
|
5
|
+
return compileString(
|
|
6
|
+
`
|
|
7
|
+
@use 'gradient' as *;
|
|
8
|
+
|
|
9
|
+
${source}
|
|
10
|
+
`,
|
|
11
|
+
{
|
|
12
|
+
loadPaths: ['src/scss/mixins'],
|
|
13
|
+
style: 'compressed',
|
|
14
|
+
},
|
|
15
|
+
).css;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe('gradient helpers', () => {
|
|
19
|
+
it.each(['top', 'right', 'bottom', 'left'] as const)(
|
|
20
|
+
'creates a smooth easing gradient toward %s',
|
|
21
|
+
direction => {
|
|
22
|
+
const css = compile(`
|
|
23
|
+
.sample {
|
|
24
|
+
background-image: oc-easing-gradient(${direction}, red, transparent);
|
|
25
|
+
}
|
|
26
|
+
`);
|
|
27
|
+
|
|
28
|
+
expect(css).toContain(`linear-gradient(to ${direction}, red 0%`);
|
|
29
|
+
expect(css).toContain(
|
|
30
|
+
'color-mix(in srgb, red 95.703125%, transparent) calc(0% + (100% - 0%) * 0.125)',
|
|
31
|
+
);
|
|
32
|
+
expect(css).toContain(
|
|
33
|
+
'color-mix(in srgb, red 50%, transparent) calc(0% + (100% - 0%) * 0.5)',
|
|
34
|
+
);
|
|
35
|
+
expect(css).toContain('transparent 100%');
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
it('applies a two-sided mask with variable fade and content sizes', () => {
|
|
40
|
+
const css = compile(`
|
|
41
|
+
.sample {
|
|
42
|
+
@include oc-gradient-mask(
|
|
43
|
+
right,
|
|
44
|
+
var(--example-start-fade-size),
|
|
45
|
+
var(--example-end-fade-size),
|
|
46
|
+
var(--example-content-size)
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
`);
|
|
50
|
+
|
|
51
|
+
expect(css).toContain('-webkit-mask-image:linear-gradient(to right');
|
|
52
|
+
expect(css).toContain('mask-image:linear-gradient(to right');
|
|
53
|
+
expect(css).toContain('transparent 0px');
|
|
54
|
+
expect(css).toContain('black var(--example-start-fade-size)');
|
|
55
|
+
expect(css).toContain(
|
|
56
|
+
'black calc(var(--example-content-size) - var(--example-end-fade-size))',
|
|
57
|
+
);
|
|
58
|
+
expect(css).toContain('transparent var(--example-content-size)');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('keeps zero-length fades valid with the default mask sizes', () => {
|
|
62
|
+
const css = compile(`
|
|
63
|
+
.sample {
|
|
64
|
+
@include oc-gradient-mask(bottom);
|
|
65
|
+
}
|
|
66
|
+
`);
|
|
67
|
+
|
|
68
|
+
expect(css).toContain('linear-gradient(to bottom, transparent 0px');
|
|
69
|
+
expect(css).toContain('black 0px');
|
|
70
|
+
expect(css).toContain('black calc(100% - 0px)');
|
|
71
|
+
expect(css).toContain('transparent 100%');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('intersects block and inline masks for four independently sized edges', () => {
|
|
75
|
+
const css = compile(`
|
|
76
|
+
.sample {
|
|
77
|
+
@include oc-gradient-mask-edges(
|
|
78
|
+
var(--example-top-fade-size),
|
|
79
|
+
var(--example-right-fade-size),
|
|
80
|
+
var(--example-bottom-fade-size),
|
|
81
|
+
var(--example-left-fade-size),
|
|
82
|
+
var(--example-inline-content-size),
|
|
83
|
+
var(--example-block-content-size)
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
`);
|
|
87
|
+
|
|
88
|
+
expect(css).toContain('-webkit-mask-composite:source-in');
|
|
89
|
+
expect(css).toContain('mask-composite:intersect');
|
|
90
|
+
expect(css).toContain('-webkit-mask-image:linear-gradient(to bottom');
|
|
91
|
+
expect(css).toContain('),linear-gradient(to right');
|
|
92
|
+
expect(css).toContain('black var(--example-top-fade-size)');
|
|
93
|
+
expect(css).toContain('black var(--example-left-fade-size)');
|
|
94
|
+
expect(css).toContain(
|
|
95
|
+
'calc(var(--example-block-content-size) - var(--example-bottom-fade-size))',
|
|
96
|
+
);
|
|
97
|
+
expect(css).toContain(
|
|
98
|
+
'calc(var(--example-inline-content-size) - var(--example-right-fade-size))',
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('rejects unsupported directions', () => {
|
|
103
|
+
expect(() =>
|
|
104
|
+
compile(`
|
|
105
|
+
.sample {
|
|
106
|
+
background-image: oc-easing-gradient(inline-start, red, blue);
|
|
107
|
+
}
|
|
108
|
+
`),
|
|
109
|
+
).toThrow('Unknown gradient direction: inline-start');
|
|
110
|
+
});
|
|
111
|
+
});
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
@use 'sass:meta';
|
|
5
5
|
@use '../foundations/responsive.config' as *;
|
|
6
6
|
@use './breakpoint' as *;
|
|
7
|
+
@use './fluid' as *;
|
|
7
8
|
@use './utils' as *;
|
|
8
9
|
|
|
9
10
|
$oc-responsive-stop-order: (min, sm, md, lg, xl, hg);
|
|
@@ -155,7 +156,7 @@ $oc-responsive-point-order: (min-sm, min-md, min-lg, min-xl, min-hg);
|
|
|
155
156
|
}
|
|
156
157
|
|
|
157
158
|
@if $oc-responsive-mode == fluid {
|
|
158
|
-
@return
|
|
159
|
+
@return oc-fluid($from, $to, $basis, $anchor);
|
|
159
160
|
}
|
|
160
161
|
|
|
161
162
|
@error "Unknown responsive mode '#{$oc-responsive-mode}'.";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
@use 'sass:list';
|
|
2
2
|
@use 'sass:map';
|
|
3
3
|
@use './breakpoint' as *;
|
|
4
|
+
@use './fluid' as *;
|
|
4
5
|
@use './utils' as *;
|
|
5
6
|
|
|
6
7
|
@mixin typo($variant, $weight: normal) {
|
|
@@ -33,7 +34,7 @@
|
|
|
33
34
|
$anchor: list.nth($value, 4);
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
$clamp:
|
|
37
|
+
$clamp: oc-fluid(toRem($from), toRem($to), $basis, $anchor);
|
|
37
38
|
|
|
38
39
|
@if $key == sm {
|
|
39
40
|
@include respond-to(sm) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
@use 'sass:math';
|
|
2
|
+
|
|
3
|
+
$rem-unit: 16;
|
|
4
|
+
|
|
5
|
+
@function toRem($px) {
|
|
6
|
+
@return math.div($px, $rem-unit) * 1rem;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
@function stripUnit($value) {
|
|
10
|
+
@if math.is-unitless($value) {
|
|
11
|
+
@return $value;
|
|
12
|
+
}
|
|
13
|
+
@return math.div($value, ($value * 0 + 1));
|
|
14
|
+
}
|
|
@@ -1,26 +1,7 @@
|
|
|
1
|
-
@use '
|
|
1
|
+
@use './fluid' as *;
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
@function toRem($px) {
|
|
6
|
-
@return math.div($px, $rem-unit) * 1rem;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
@function stripUnit($value) {
|
|
10
|
-
@if math.is-unitless($value) {
|
|
11
|
-
@return $value;
|
|
12
|
-
}
|
|
13
|
-
@return math.div($value, ($value * 0 + 1));
|
|
14
|
-
}
|
|
3
|
+
@forward './units';
|
|
15
4
|
|
|
16
5
|
@function fluidClamp($from, $bp, $anchor: to, $to) {
|
|
17
|
-
$
|
|
18
|
-
@if $anchor == from {
|
|
19
|
-
$basis: $from;
|
|
20
|
-
}
|
|
21
|
-
$basis-num: stripUnit($basis);
|
|
22
|
-
$bp-num: stripUnit($bp);
|
|
23
|
-
$basis-px: $basis-num * $rem-unit;
|
|
24
|
-
|
|
25
|
-
@return clamp(#{$from}, calc(#{$basis-px} / #{$bp-num} * 100vw), #{$to});
|
|
6
|
+
@return oc-fluid($from, $to, $bp, $anchor);
|
|
26
7
|
}
|