@primer/primitives 11.4.0 → 11.4.1-rc.0499d5a6
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/DESIGN_TOKENS_GUIDE.md +185 -0
- package/DESIGN_TOKENS_SPEC.md +565 -0
- package/dist/build/formats/jsonFigma.js +8 -1
- package/dist/build/formats/markdownLlmGuidelines.d.ts +7 -6
- package/dist/build/formats/markdownLlmGuidelines.js +1034 -60
- package/dist/build/schemas/borderToken.d.ts +61 -5
- package/dist/build/schemas/borderToken.js +2 -1
- package/dist/build/schemas/colorToken.d.ts +639 -30
- package/dist/build/schemas/colorToken.js +3 -2
- package/dist/build/schemas/colorW3cValue.d.ts +28 -0
- package/dist/build/schemas/colorW3cValue.js +42 -0
- package/dist/build/schemas/cubicBezierToken.d.ts +1 -1
- package/dist/build/schemas/dimensionToken.d.ts +9 -2
- package/dist/build/schemas/dimensionValue.d.ts +12 -1
- package/dist/build/schemas/dimensionValue.js +10 -13
- package/dist/build/schemas/durationToken.d.ts +8 -2
- package/dist/build/schemas/durationValue.d.ts +11 -1
- package/dist/build/schemas/durationValue.js +13 -3
- package/dist/build/schemas/fontFamilyToken.d.ts +1 -1
- package/dist/build/schemas/fontWeightToken.d.ts +1 -1
- package/dist/build/schemas/gradientToken.d.ts +23 -2
- package/dist/build/schemas/gradientToken.js +2 -1
- package/dist/build/schemas/numberToken.d.ts +1 -1
- package/dist/build/schemas/shadowToken.d.ts +1751 -127
- package/dist/build/schemas/shadowToken.js +8 -2
- package/dist/build/schemas/stringToken.d.ts +1 -1
- package/dist/build/schemas/stringToken.js +1 -1
- package/dist/build/schemas/tokenType.d.ts +1 -1
- package/dist/build/schemas/transitionToken.d.ts +15 -3
- package/dist/build/schemas/typographyToken.d.ts +19 -5
- package/dist/build/schemas/typographyToken.js +1 -1
- package/dist/build/schemas/validTokenType.d.ts +1 -1
- package/dist/build/schemas/validTokenType.js +1 -1
- package/dist/build/schemas/viewportRangeToken.d.ts +1 -1
- package/dist/build/transformers/borderToCss.js +19 -1
- package/dist/build/transformers/colorAlphaToCss.js +6 -3
- package/dist/build/transformers/colorToHex.js +5 -2
- package/dist/build/transformers/colorToRgbAlpha.js +5 -2
- package/dist/build/transformers/colorToRgbaFloat.js +5 -0
- package/dist/build/transformers/dimensionToPixelUnitless.d.ts +3 -2
- package/dist/build/transformers/dimensionToPixelUnitless.js +22 -26
- package/dist/build/transformers/dimensionToRem.d.ts +2 -1
- package/dist/build/transformers/dimensionToRem.js +21 -22
- package/dist/build/transformers/dimensionToRemPxArray.d.ts +2 -1
- package/dist/build/transformers/dimensionToRemPxArray.js +21 -22
- package/dist/build/transformers/durationToCss.d.ts +2 -1
- package/dist/build/transformers/durationToCss.js +18 -11
- package/dist/build/transformers/gradientToCss.js +2 -1
- package/dist/build/transformers/shadowToCss.js +15 -1
- package/dist/build/transformers/utilities/normalizeColorValue.d.ts +23 -0
- package/dist/build/transformers/utilities/normalizeColorValue.js +74 -0
- package/dist/build/transformers/utilities/parseDimension.d.ts +12 -0
- package/dist/build/transformers/utilities/parseDimension.js +31 -0
- package/dist/build/types/borderTokenValue.d.ts +5 -2
- package/dist/build/types/dimensionTokenValue.d.ts +9 -0
- package/dist/build/types/shadowTokenValue.d.ts +8 -5
- package/dist/css/functional/themes/dark-colorblind-high-contrast.css +32 -28
- package/dist/css/functional/themes/dark-colorblind.css +32 -28
- package/dist/css/functional/themes/dark-dimmed-high-contrast.css +32 -28
- package/dist/css/functional/themes/dark-dimmed.css +32 -28
- package/dist/css/functional/themes/dark-high-contrast.css +32 -28
- package/dist/css/functional/themes/dark-tritanopia-high-contrast.css +32 -28
- package/dist/css/functional/themes/dark-tritanopia.css +32 -28
- package/dist/css/functional/themes/dark.css +32 -28
- package/dist/css/functional/themes/light-colorblind-high-contrast.css +32 -28
- package/dist/css/functional/themes/light-colorblind.css +32 -28
- package/dist/css/functional/themes/light-high-contrast.css +32 -28
- package/dist/css/functional/themes/light-tritanopia-high-contrast.css +32 -28
- package/dist/css/functional/themes/light-tritanopia.css +32 -28
- package/dist/css/functional/themes/light.css +32 -28
- package/dist/css/primitives.css +4 -0
- package/dist/docs/base/motion/motion.json +96 -24
- package/dist/docs/base/size/size.json +76 -19
- package/dist/docs/base/typography/typography.json +24 -6
- package/dist/docs/functional/size/border.json +26 -11
- package/dist/docs/functional/size/breakpoints.json +24 -6
- package/dist/docs/functional/size/radius.json +16 -4
- package/dist/docs/functional/size/size.json +60 -15
- package/dist/docs/functional/themes/dark-colorblind-high-contrast.json +2629 -346
- package/dist/docs/functional/themes/dark-colorblind.json +2629 -346
- package/dist/docs/functional/themes/dark-dimmed-high-contrast.json +2629 -346
- package/dist/docs/functional/themes/dark-dimmed.json +2629 -346
- package/dist/docs/functional/themes/dark-high-contrast.json +2629 -346
- package/dist/docs/functional/themes/dark-tritanopia-high-contrast.json +2629 -346
- package/dist/docs/functional/themes/dark-tritanopia.json +2629 -346
- package/dist/docs/functional/themes/dark.json +2629 -346
- package/dist/docs/functional/themes/light-colorblind-high-contrast.json +2635 -352
- package/dist/docs/functional/themes/light-colorblind.json +2632 -349
- package/dist/docs/functional/themes/light-high-contrast.json +2635 -352
- package/dist/docs/functional/themes/light-tritanopia-high-contrast.json +2635 -352
- package/dist/docs/functional/themes/light-tritanopia.json +2632 -349
- package/dist/docs/functional/themes/light.json +2632 -349
- package/dist/docs/functional/typography/typography.json +8 -2
- package/dist/fallbacks/base/motion/motion.json +48 -12
- package/dist/figma/themes/light-colorblind.json +4 -4
- package/dist/figma/themes/light-high-contrast.json +4 -4
- package/dist/figma/themes/light-tritanopia.json +4 -4
- package/dist/figma/themes/light.json +4 -4
- package/dist/internalCss/dark-colorblind-high-contrast.css +28 -28
- package/dist/internalCss/dark-colorblind.css +28 -28
- package/dist/internalCss/dark-dimmed-high-contrast.css +28 -28
- package/dist/internalCss/dark-dimmed.css +28 -28
- package/dist/internalCss/dark-high-contrast.css +28 -28
- package/dist/internalCss/dark-tritanopia-high-contrast.css +28 -28
- package/dist/internalCss/dark-tritanopia.css +28 -28
- package/dist/internalCss/dark.css +28 -28
- package/dist/internalCss/light-colorblind-high-contrast.css +28 -28
- package/dist/internalCss/light-colorblind.css +28 -28
- package/dist/internalCss/light-high-contrast.css +28 -28
- package/dist/internalCss/light-tritanopia-high-contrast.css +28 -28
- package/dist/internalCss/light-tritanopia.css +28 -28
- package/dist/internalCss/light.css +28 -28
- package/dist/styleLint/base/motion/motion.json +96 -24
- package/dist/styleLint/base/size/size.json +76 -19
- package/dist/styleLint/base/typography/typography.json +30 -12
- package/dist/styleLint/functional/size/border.json +27 -12
- package/dist/styleLint/functional/size/breakpoints.json +24 -6
- package/dist/styleLint/functional/size/radius.json +17 -5
- package/dist/styleLint/functional/size/size-coarse.json +3 -3
- package/dist/styleLint/functional/size/size-fine.json +3 -3
- package/dist/styleLint/functional/size/size.json +111 -66
- package/dist/styleLint/functional/themes/dark-colorblind-high-contrast.json +2757 -366
- package/dist/styleLint/functional/themes/dark-colorblind.json +2757 -366
- package/dist/styleLint/functional/themes/dark-dimmed-high-contrast.json +2757 -366
- package/dist/styleLint/functional/themes/dark-dimmed.json +2757 -366
- package/dist/styleLint/functional/themes/dark-high-contrast.json +2757 -366
- package/dist/styleLint/functional/themes/dark-tritanopia-high-contrast.json +2757 -366
- package/dist/styleLint/functional/themes/dark-tritanopia.json +2757 -366
- package/dist/styleLint/functional/themes/dark.json +2757 -366
- package/dist/styleLint/functional/themes/light-colorblind-high-contrast.json +2763 -372
- package/dist/styleLint/functional/themes/light-colorblind.json +2760 -369
- package/dist/styleLint/functional/themes/light-high-contrast.json +2763 -372
- package/dist/styleLint/functional/themes/light-tritanopia-high-contrast.json +2763 -372
- package/dist/styleLint/functional/themes/light-tritanopia.json +2760 -369
- package/dist/styleLint/functional/themes/light.json +2760 -369
- package/dist/styleLint/functional/typography/typography.json +28 -22
- package/package.json +9 -7
- package/src/tokens/base/motion/timing.json5 +12 -12
- package/src/tokens/base/size/size.json5 +194 -194
- package/src/tokens/base/typography/typography.json5 +6 -6
- package/src/tokens/component/avatar.json5 +72 -44
- package/src/tokens/component/button.json5 +1545 -1193
- package/src/tokens/functional/border/border.json5 +4 -1
- package/src/tokens/functional/color/bgColor.json5 +8 -0
- package/src/tokens/functional/color/display.json5 +7 -0
- package/src/tokens/functional/color/fgColor.json5 +8 -0
- package/src/tokens/functional/color/syntax.json5 +14 -0
- package/src/tokens/functional/shadow/shadow.json5 +1254 -163
- package/src/tokens/functional/size/border.json5 +8 -8
- package/src/tokens/functional/size/breakpoints.json5 +6 -6
- package/src/tokens/functional/size/radius.json5 +4 -4
- package/src/tokens/functional/size/size.json5 +15 -15
- package/src/tokens/functional/typography/typography.json5 +8 -4
- package/dist/build/parsers/index.d.ts +0 -1
- package/dist/build/parsers/index.js +0 -1
- package/dist/build/parsers/w3cJsonParser.d.ts +0 -6
- package/dist/build/parsers/w3cJsonParser.js +0 -25
- package/dist/removed/testing.json5 +0 -4
- package/guidelines/color.llm.md +0 -16
- package/guidelines/guidelines.llm.md +0 -34
- package/guidelines/motion.llm.md +0 -41
- package/guidelines/spacing.llm.md +0 -20
- package/guidelines/typography.llm.md +0 -14
- package/src/tokens/removed/testing.json5 +0 -4
- package/token-guidelines.llm.md +0 -695
|
@@ -2,6 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { baseToken } from './baseToken.js';
|
|
3
3
|
import { referenceValue } from './referenceValue.js';
|
|
4
4
|
import { colorHexValue } from './colorHexValue.js';
|
|
5
|
+
import { colorW3cValue } from './colorW3cValue.js';
|
|
5
6
|
import { alphaValue } from './alphaValue.js';
|
|
6
7
|
import { dimensionValue } from './dimensionValue.js';
|
|
7
8
|
import { tokenType } from './tokenType.js';
|
|
@@ -9,8 +10,8 @@ import { collection, mode } from './collections.js';
|
|
|
9
10
|
import { llmExtension } from './llmExtension.js';
|
|
10
11
|
export const shadowValue = z
|
|
11
12
|
.object({
|
|
12
|
-
color: z.union([colorHexValue, referenceValue]),
|
|
13
|
-
alpha: z.union([alphaValue, referenceValue]),
|
|
13
|
+
color: z.union([colorHexValue, colorW3cValue, referenceValue]),
|
|
14
|
+
alpha: z.union([alphaValue, referenceValue]).optional(),
|
|
14
15
|
offsetX: z.union([dimensionValue, referenceValue]),
|
|
15
16
|
offsetY: z.union([dimensionValue, referenceValue]),
|
|
16
17
|
blur: z.union([dimensionValue, referenceValue]),
|
|
@@ -51,11 +52,16 @@ export const shadowToken = baseToken
|
|
|
51
52
|
'light-tritanopia': override,
|
|
52
53
|
'light-protanopia-deuteranopia': override,
|
|
53
54
|
'light-high-contrast': override,
|
|
55
|
+
'light-tritanopia-high-contrast': override,
|
|
56
|
+
'light-protanopia-deuteranopia-high-contrast': override,
|
|
54
57
|
dark: override,
|
|
55
58
|
'dark-tritanopia': override,
|
|
56
59
|
'dark-protanopia-deuteranopia': override,
|
|
57
60
|
'dark-high-contrast': override,
|
|
61
|
+
'dark-tritanopia-high-contrast': override,
|
|
62
|
+
'dark-protanopia-deuteranopia-high-contrast': override,
|
|
58
63
|
'dark-dimmed': override,
|
|
64
|
+
'dark-dimmed-high-contrast': override,
|
|
59
65
|
})
|
|
60
66
|
.strict()
|
|
61
67
|
.optional(),
|
|
@@ -3,7 +3,7 @@ export declare const stringToken: z.ZodObject<{
|
|
|
3
3
|
$description: z.ZodOptional<z.ZodString>;
|
|
4
4
|
$deprecated: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>>;
|
|
5
5
|
$value: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
|
|
6
|
-
$type: z.ZodLiteral<"
|
|
6
|
+
$type: z.ZodLiteral<"number" | "border" | "color" | "fontFamily" | "fontWeight" | "transition" | "duration" | "dimension" | "gradient" | "shadow" | "typography" | "cubicBezier" | "custom-viewportRange" | "custom-string">;
|
|
7
7
|
$extensions: z.ZodOptional<z.ZodObject<{
|
|
8
8
|
'org.primer.llm': z.ZodOptional<z.ZodObject<{
|
|
9
9
|
usage: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -6,7 +6,7 @@ import { llmExtension } from './llmExtension.js';
|
|
|
6
6
|
export const stringToken = baseToken
|
|
7
7
|
.extend({
|
|
8
8
|
$value: z.union([z.string(), referenceValue]),
|
|
9
|
-
$type: tokenType('string'),
|
|
9
|
+
$type: tokenType('custom-string'),
|
|
10
10
|
$extensions: z
|
|
11
11
|
.object({
|
|
12
12
|
'org.primer.llm': llmExtension,
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import type { TokenType } from './validTokenType.js';
|
|
3
|
-
export declare const tokenType: ($type: TokenType) => z.ZodLiteral<"
|
|
3
|
+
export declare const tokenType: ($type: TokenType) => z.ZodLiteral<"number" | "border" | "color" | "fontFamily" | "fontWeight" | "transition" | "duration" | "dimension" | "gradient" | "shadow" | "typography" | "cubicBezier" | "custom-viewportRange" | "custom-string">;
|
|
@@ -3,11 +3,23 @@ export declare const transitionToken: z.ZodObject<{
|
|
|
3
3
|
$description: z.ZodOptional<z.ZodString>;
|
|
4
4
|
$deprecated: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>>;
|
|
5
5
|
$value: z.ZodUnion<readonly [z.ZodObject<{
|
|
6
|
-
duration: z.ZodUnion<readonly [z.ZodUnion<readonly [z.
|
|
6
|
+
duration: z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodObject<{
|
|
7
|
+
value: z.ZodNumber;
|
|
8
|
+
unit: z.ZodEnum<{
|
|
9
|
+
s: "s";
|
|
10
|
+
ms: "ms";
|
|
11
|
+
}>;
|
|
12
|
+
}, z.core.$strict>, z.ZodString]>, z.ZodString]>;
|
|
7
13
|
timingFunction: z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodArray<z.ZodNumber>, z.ZodString]>, z.ZodString]>;
|
|
8
|
-
delay: z.ZodOptional<z.ZodUnion<readonly [z.ZodUnion<readonly [z.
|
|
14
|
+
delay: z.ZodOptional<z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodObject<{
|
|
15
|
+
value: z.ZodNumber;
|
|
16
|
+
unit: z.ZodEnum<{
|
|
17
|
+
s: "s";
|
|
18
|
+
ms: "ms";
|
|
19
|
+
}>;
|
|
20
|
+
}, z.core.$strict>, z.ZodString]>, z.ZodString]>>;
|
|
9
21
|
}, z.core.$strip>, z.ZodString]>;
|
|
10
|
-
$type: z.ZodLiteral<"
|
|
22
|
+
$type: z.ZodLiteral<"number" | "border" | "color" | "fontFamily" | "fontWeight" | "transition" | "duration" | "dimension" | "gradient" | "shadow" | "typography" | "cubicBezier" | "custom-viewportRange" | "custom-string">;
|
|
11
23
|
$extensions: z.ZodOptional<z.ZodObject<{
|
|
12
24
|
'org.primer.llm': z.ZodOptional<z.ZodObject<{
|
|
13
25
|
usage: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export declare const typographyValue: z.ZodObject<{
|
|
3
|
-
fontSize: z.ZodUnion<readonly [z.
|
|
4
|
-
|
|
3
|
+
fontSize: z.ZodUnion<readonly [z.ZodObject<{
|
|
4
|
+
value: z.ZodNumber;
|
|
5
|
+
unit: z.ZodEnum<{
|
|
6
|
+
em: "em";
|
|
7
|
+
px: "px";
|
|
8
|
+
rem: "rem";
|
|
9
|
+
}>;
|
|
10
|
+
}, z.core.$strict>, z.ZodString]>;
|
|
11
|
+
lineHeight: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
5
12
|
fontWeight: z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>;
|
|
6
13
|
fontFamily: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
|
|
7
14
|
}, z.core.$strip>;
|
|
@@ -9,12 +16,19 @@ export declare const typographyToken: z.ZodObject<{
|
|
|
9
16
|
$description: z.ZodOptional<z.ZodString>;
|
|
10
17
|
$deprecated: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>>;
|
|
11
18
|
$value: z.ZodUnion<readonly [z.ZodObject<{
|
|
12
|
-
fontSize: z.ZodUnion<readonly [z.
|
|
13
|
-
|
|
19
|
+
fontSize: z.ZodUnion<readonly [z.ZodObject<{
|
|
20
|
+
value: z.ZodNumber;
|
|
21
|
+
unit: z.ZodEnum<{
|
|
22
|
+
em: "em";
|
|
23
|
+
px: "px";
|
|
24
|
+
rem: "rem";
|
|
25
|
+
}>;
|
|
26
|
+
}, z.core.$strict>, z.ZodString]>;
|
|
27
|
+
lineHeight: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
14
28
|
fontWeight: z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>;
|
|
15
29
|
fontFamily: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
|
|
16
30
|
}, z.core.$strip>, z.ZodString]>;
|
|
17
|
-
$type: z.ZodLiteral<"
|
|
31
|
+
$type: z.ZodLiteral<"number" | "border" | "color" | "fontFamily" | "fontWeight" | "transition" | "duration" | "dimension" | "gradient" | "shadow" | "typography" | "cubicBezier" | "custom-viewportRange" | "custom-string">;
|
|
18
32
|
$extensions: z.ZodOptional<z.ZodObject<{
|
|
19
33
|
'org.primer.llm': z.ZodOptional<z.ZodObject<{
|
|
20
34
|
usage: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -7,7 +7,7 @@ import { tokenType } from './tokenType.js';
|
|
|
7
7
|
import { llmExtension } from './llmExtension.js';
|
|
8
8
|
export const typographyValue = z.object({
|
|
9
9
|
fontSize: z.union([dimensionValue, referenceValue]),
|
|
10
|
-
lineHeight: z.union([
|
|
10
|
+
lineHeight: z.union([z.number(), referenceValue]).optional(),
|
|
11
11
|
fontWeight: z.union([fontWeightValue, referenceValue]),
|
|
12
12
|
fontFamily: z.union([z.string().min(1), referenceValue]),
|
|
13
13
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
declare const validTypes: readonly ["color", "cubicBezier", "typography", "dimension", "duration", "border", "shadow", "fontFamily", "fontWeight", "gradient", "number", "string", "transition", "custom-viewportRange"];
|
|
2
|
+
declare const validTypes: readonly ["color", "cubicBezier", "typography", "dimension", "duration", "border", "shadow", "fontFamily", "fontWeight", "gradient", "number", "custom-string", "transition", "custom-viewportRange"];
|
|
3
3
|
export type TokenType = (typeof validTypes)[number];
|
|
4
4
|
/**
|
|
5
5
|
* Recursively validates $type properties in token structure
|
|
@@ -3,7 +3,7 @@ export declare const viewportRangeToken: z.ZodObject<{
|
|
|
3
3
|
$description: z.ZodOptional<z.ZodString>;
|
|
4
4
|
$deprecated: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>>;
|
|
5
5
|
$value: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
|
|
6
|
-
$type: z.ZodLiteral<"
|
|
6
|
+
$type: z.ZodLiteral<"number" | "border" | "color" | "fontFamily" | "fontWeight" | "transition" | "duration" | "dimension" | "gradient" | "shadow" | "typography" | "cubicBezier" | "custom-viewportRange" | "custom-string">;
|
|
7
7
|
$extensions: z.ZodOptional<z.ZodObject<{
|
|
8
8
|
'org.primer.llm': z.ZodOptional<z.ZodObject<{
|
|
9
9
|
usage: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
import { isBorder } from '../filters/isBorder.js';
|
|
2
|
+
import { parseDimension } from './utilities/parseDimension.js';
|
|
3
|
+
import { normalizeColorValue } from './utilities/normalizeColorValue.js';
|
|
4
|
+
/**
|
|
5
|
+
* @description Converts a W3C dimension object to CSS string, preserving the original unit
|
|
6
|
+
* @param dim - The dimension value in W3C object format or a string
|
|
7
|
+
* @returns CSS dimension string (e.g., "2px", "0.125rem", "1em", "0")
|
|
8
|
+
*/
|
|
9
|
+
const dimensionToCss = (dim) => {
|
|
10
|
+
if (typeof dim === 'string') {
|
|
11
|
+
return dim;
|
|
12
|
+
}
|
|
13
|
+
const { value, unit } = parseDimension(dim);
|
|
14
|
+
if (value === 0) {
|
|
15
|
+
return '0';
|
|
16
|
+
}
|
|
17
|
+
return `${value}${unit}`;
|
|
18
|
+
};
|
|
2
19
|
/**
|
|
3
20
|
* checks if all required properties exist on shadow token
|
|
4
21
|
* @param object - BorderTokenValue
|
|
@@ -33,6 +50,7 @@ export const borderToCss = {
|
|
|
33
50
|
throw new Error(`Invalid border token property ${JSON.stringify(value)}. Must be an object with color, width and style properties.`);
|
|
34
51
|
}
|
|
35
52
|
/* width | style | color */
|
|
36
|
-
|
|
53
|
+
const color = typeof value.color === 'object' ? normalizeColorValue(value.color) : value.color;
|
|
54
|
+
return `${dimensionToCss(value.width)} ${value.style} ${color}`;
|
|
37
55
|
},
|
|
38
56
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isColorWithAlpha } from '../filters/isColorWithAlpha.js';
|
|
2
2
|
import { getTokenValue } from './utilities/getTokenValue.js';
|
|
3
|
+
import { normalizeColorValue } from './utilities/normalizeColorValue.js';
|
|
3
4
|
export const cssColorMix = (colorA, colorB, colorBPercent) => {
|
|
4
5
|
if (colorBPercent < 0 || colorBPercent > 1) {
|
|
5
6
|
throw new Error(`Invalid argument for "cssColorMix", colorBPercent must be between 0 and 1, ${colorBPercent} provided.`);
|
|
@@ -16,8 +17,10 @@ export const colorAlphaToCss = {
|
|
|
16
17
|
transitive: true,
|
|
17
18
|
filter: isColorWithAlpha,
|
|
18
19
|
transform: (token) => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const rawValue = getTokenValue(token);
|
|
21
|
+
const colorString = normalizeColorValue(rawValue);
|
|
22
|
+
if (token.alpha === null || token.alpha === undefined)
|
|
23
|
+
return colorString;
|
|
24
|
+
return cssColorMix(colorString, 'transparent', 1 - token.alpha);
|
|
22
25
|
},
|
|
23
26
|
};
|
|
@@ -2,6 +2,7 @@ import { toHex } from 'color2k';
|
|
|
2
2
|
import { isColor } from '../filters/index.js';
|
|
3
3
|
import { getTokenValue } from './utilities/getTokenValue.js';
|
|
4
4
|
import { alpha } from './utilities/alpha.js';
|
|
5
|
+
import { normalizeColorValue } from './utilities/normalizeColorValue.js';
|
|
5
6
|
/**
|
|
6
7
|
* @description converts color tokens value to `hex6` or `hex8`
|
|
7
8
|
* @type value transformer — [StyleDictionary.ValueTransform](https://github.com/amzn/style-dictionary/blob/main/types/Transform.d.ts)
|
|
@@ -14,10 +15,12 @@ export const colorToHex = {
|
|
|
14
15
|
transitive: true,
|
|
15
16
|
filter: isColor,
|
|
16
17
|
transform: (token, config) => {
|
|
18
|
+
const rawValue = getTokenValue(token);
|
|
19
|
+
const colorString = normalizeColorValue(rawValue);
|
|
17
20
|
const alphaValue = token.alpha;
|
|
18
21
|
if (alphaValue === null || alphaValue === undefined || alphaValue === 1) {
|
|
19
|
-
return toHex(
|
|
22
|
+
return toHex(colorString);
|
|
20
23
|
}
|
|
21
|
-
return toHex(alpha(
|
|
24
|
+
return toHex(alpha(colorString, alphaValue, token, config));
|
|
22
25
|
},
|
|
23
26
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isColorWithAlpha } from '../filters/index.js';
|
|
2
2
|
import { alpha } from './utilities/alpha.js';
|
|
3
3
|
import { getTokenValue } from './utilities/getTokenValue.js';
|
|
4
|
+
import { normalizeColorValue } from './utilities/normalizeColorValue.js';
|
|
4
5
|
/**
|
|
5
6
|
* @description replaces tokens value with `rgba` color using the tokens `alpha` property to specify the value used for alpha
|
|
6
7
|
* @type value transformer — [StyleDictionary.ValueTransform](https://github.com/amzn/style-dictionary/blob/main/types/Transform.d.ts)
|
|
@@ -13,8 +14,10 @@ export const colorToRgbAlpha = {
|
|
|
13
14
|
transitive: true,
|
|
14
15
|
filter: isColorWithAlpha,
|
|
15
16
|
transform: (token, config) => {
|
|
17
|
+
const rawValue = getTokenValue(token);
|
|
18
|
+
const colorString = normalizeColorValue(rawValue);
|
|
16
19
|
if (token.alpha === null)
|
|
17
|
-
return
|
|
18
|
-
return alpha(
|
|
20
|
+
return colorString;
|
|
21
|
+
return alpha(colorString, token.alpha, token, config);
|
|
19
22
|
},
|
|
20
23
|
};
|
|
@@ -4,8 +4,13 @@ import { getTokenValue } from './utilities/getTokenValue.js';
|
|
|
4
4
|
import { rgbaFloatToHex } from './utilities/rgbaFloatToHex.js';
|
|
5
5
|
import { hexToRgbaFloat } from './utilities/hexToRgbaFloat.js';
|
|
6
6
|
import { isRgbaFloat } from './utilities/isRgbaFloat.js';
|
|
7
|
+
import { normalizeColorValue, isW3cColorValue } from './utilities/normalizeColorValue.js';
|
|
7
8
|
const toRgbaFloat = (token, alpha = undefined) => {
|
|
8
9
|
let tokenValue = getTokenValue(token);
|
|
10
|
+
// Handle W3C color object - convert to hex first
|
|
11
|
+
if (isW3cColorValue(tokenValue)) {
|
|
12
|
+
tokenValue = normalizeColorValue(tokenValue);
|
|
13
|
+
}
|
|
9
14
|
// get hex value from color string
|
|
10
15
|
if (isRgbaFloat(tokenValue)) {
|
|
11
16
|
tokenValue = rgbaFloatToHex(tokenValue, false);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Transform } from 'style-dictionary/types';
|
|
2
2
|
/**
|
|
3
|
-
* @description converts dimension tokens value to pixel value without unit
|
|
3
|
+
* @description converts dimension tokens value to pixel value without unit
|
|
4
4
|
* @type value transformer — [StyleDictionary.ValueTransform](https://github.com/amzn/style-dictionary/blob/main/types/Transform.d.ts)
|
|
5
5
|
* @matcher matches all tokens of $type `dimension`
|
|
6
|
-
* @transformer returns a
|
|
6
|
+
* @transformer returns a number for px/rem values, or a string with unit for em values (cannot convert to unitless)
|
|
7
|
+
* @note Expects W3C DTCG format { value: number, unit: "px" | "rem" | "em" }
|
|
7
8
|
*/
|
|
8
9
|
export declare const dimensionToPixelUnitless: Transform;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isDimension } from '../filters/index.js';
|
|
2
|
+
import { parseDimension } from './utilities/parseDimension.js';
|
|
2
3
|
/**
|
|
3
4
|
* @description base font size from options or 16
|
|
4
5
|
* @param options
|
|
@@ -6,22 +7,11 @@ import { isDimension } from '../filters/index.js';
|
|
|
6
7
|
*/
|
|
7
8
|
const getBasePxFontSize = (options) => (options && options.basePxFontSize) || 16;
|
|
8
9
|
/**
|
|
9
|
-
* @description
|
|
10
|
-
* @param value token value
|
|
11
|
-
* @param unit unit string like px or value
|
|
12
|
-
* @returns boolean
|
|
13
|
-
*/
|
|
14
|
-
const hasUnit = (value, unit) => {
|
|
15
|
-
if (typeof value === 'number') {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
return value.indexOf(unit) > -1;
|
|
19
|
-
};
|
|
20
|
-
/**
|
|
21
|
-
* @description converts dimension tokens value to pixel value without unit, ignores `em` as they are relative to the font size of the parent element
|
|
10
|
+
* @description converts dimension tokens value to pixel value without unit
|
|
22
11
|
* @type value transformer — [StyleDictionary.ValueTransform](https://github.com/amzn/style-dictionary/blob/main/types/Transform.d.ts)
|
|
23
12
|
* @matcher matches all tokens of $type `dimension`
|
|
24
|
-
* @transformer returns a
|
|
13
|
+
* @transformer returns a number for px/rem values, or a string with unit for em values (cannot convert to unitless)
|
|
14
|
+
* @note Expects W3C DTCG format { value: number, unit: "px" | "rem" | "em" }
|
|
25
15
|
*/
|
|
26
16
|
export const dimensionToPixelUnitless = {
|
|
27
17
|
name: 'dimension/pixelUnitless',
|
|
@@ -31,19 +21,25 @@ export const dimensionToPixelUnitless = {
|
|
|
31
21
|
transform: (token, config, options) => {
|
|
32
22
|
const valueProp = options.usesDtcg ? '$value' : 'value';
|
|
33
23
|
const baseFont = getBasePxFontSize(config);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
24
|
+
try {
|
|
25
|
+
const { value, unit } = parseDimension(token[valueProp]);
|
|
26
|
+
if (value === 0) {
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
// rem values convert to px
|
|
30
|
+
if (unit === 'rem') {
|
|
31
|
+
return value * baseFont;
|
|
32
|
+
}
|
|
33
|
+
// em values pass through as string (relative to parent, cannot convert to unitless px)
|
|
34
|
+
if (unit === 'em') {
|
|
35
|
+
return `${value}em`;
|
|
36
|
+
}
|
|
37
|
+
// px values return the number directly
|
|
38
|
+
return value;
|
|
43
39
|
}
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
catch (error) {
|
|
41
|
+
const originalMessage = error instanceof Error ? error.message : String(error);
|
|
42
|
+
throw new Error(`Invalid dimension token: '${token.path.join('.')}: ${JSON.stringify(token[valueProp])}' - ${originalMessage}\n`);
|
|
46
43
|
}
|
|
47
|
-
return token[valueProp];
|
|
48
44
|
},
|
|
49
45
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Transform } from 'style-dictionary/types';
|
|
2
2
|
/**
|
|
3
|
-
* @description converts dimension tokens value to `rem
|
|
3
|
+
* @description converts dimension tokens value to `rem`
|
|
4
4
|
* @type value transformer — [StyleDictionary.ValueTransform](https://github.com/amzn/style-dictionary/blob/main/types/Transform.d.ts)
|
|
5
5
|
* @matcher matches all tokens of $type `dimension`
|
|
6
6
|
* @transformer returns a `rem` string
|
|
7
|
+
* @note Expects W3C DTCG format { value: number, unit: "px" | "rem" | "em" }
|
|
7
8
|
*/
|
|
8
9
|
export declare const dimensionToRem: Transform;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isDimension } from '../filters/index.js';
|
|
2
|
+
import { parseDimension } from './utilities/parseDimension.js';
|
|
2
3
|
/**
|
|
3
4
|
* @description base font size from options or 16
|
|
4
5
|
* @param options
|
|
@@ -6,22 +7,11 @@ import { isDimension } from '../filters/index.js';
|
|
|
6
7
|
*/
|
|
7
8
|
const getBasePxFontSize = (options) => (options && options.basePxFontSize) || 16;
|
|
8
9
|
/**
|
|
9
|
-
* @description
|
|
10
|
-
* @param value token value
|
|
11
|
-
* @param unit unit string like px or value
|
|
12
|
-
* @returns boolean
|
|
13
|
-
*/
|
|
14
|
-
const hasUnit = (value, unit) => {
|
|
15
|
-
if (typeof value === 'number') {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
return value.indexOf(unit) > -1;
|
|
19
|
-
};
|
|
20
|
-
/**
|
|
21
|
-
* @description converts dimension tokens value to `rem`, ignores `em` as they are relative to the font size of the parent element
|
|
10
|
+
* @description converts dimension tokens value to `rem`
|
|
22
11
|
* @type value transformer — [StyleDictionary.ValueTransform](https://github.com/amzn/style-dictionary/blob/main/types/Transform.d.ts)
|
|
23
12
|
* @matcher matches all tokens of $type `dimension`
|
|
24
13
|
* @transformer returns a `rem` string
|
|
14
|
+
* @note Expects W3C DTCG format { value: number, unit: "px" | "rem" | "em" }
|
|
25
15
|
*/
|
|
26
16
|
export const dimensionToRem = {
|
|
27
17
|
name: 'dimension/rem',
|
|
@@ -31,16 +21,25 @@ export const dimensionToRem = {
|
|
|
31
21
|
transform: (token, config, options) => {
|
|
32
22
|
const valueProp = options.usesDtcg ? '$value' : 'value';
|
|
33
23
|
const baseFont = getBasePxFontSize(config);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
24
|
+
try {
|
|
25
|
+
const { value, unit } = parseDimension(token[valueProp]);
|
|
26
|
+
if (value === 0) {
|
|
27
|
+
return '0';
|
|
28
|
+
}
|
|
29
|
+
// rem values pass through unchanged
|
|
30
|
+
if (unit === 'rem') {
|
|
31
|
+
return `${value}rem`;
|
|
32
|
+
}
|
|
33
|
+
// em values pass through unchanged (relative to parent, cannot convert)
|
|
34
|
+
if (unit === 'em') {
|
|
35
|
+
return `${value}em`;
|
|
36
|
+
}
|
|
37
|
+
// px values convert to rem
|
|
38
|
+
return `${value / baseFont}rem`;
|
|
40
39
|
}
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
catch (error) {
|
|
41
|
+
const details = error instanceof Error && error.message ? ` - ${error.message}` : error ? ` - ${String(error)}` : '';
|
|
42
|
+
throw new Error(`Invalid dimension token: '${token.name}: ${JSON.stringify(token[valueProp])}' is not valid and cannot be transformed to 'rem'${details}\n`);
|
|
43
43
|
}
|
|
44
|
-
return `${floatVal / baseFont}rem`;
|
|
45
44
|
},
|
|
46
45
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Transform } from 'style-dictionary/types';
|
|
2
2
|
/**
|
|
3
|
-
* @description converts dimension tokens value to `rem
|
|
3
|
+
* @description converts dimension tokens value to an array with both `rem` and `px` values
|
|
4
4
|
* @type value transformer — [StyleDictionary.ValueTransform](https://github.com/amzn/style-dictionary/blob/main/types/Transform.d.ts)
|
|
5
5
|
* @matcher matches all tokens of $type `dimension`
|
|
6
6
|
* @transformer returns an array with the `rem` and `pixel` string
|
|
7
|
+
* @note Expects W3C DTCG format { value: number, unit: "px" | "rem" | "em" }
|
|
7
8
|
*/
|
|
8
9
|
export declare const dimensionToRemPxArray: Transform;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isDimension } from '../filters/index.js';
|
|
2
|
+
import { parseDimension } from './utilities/parseDimension.js';
|
|
2
3
|
/**
|
|
3
4
|
* @description base font size from options or 16
|
|
4
5
|
* @param options
|
|
@@ -6,22 +7,11 @@ import { isDimension } from '../filters/index.js';
|
|
|
6
7
|
*/
|
|
7
8
|
const getBasePxFontSize = (options) => (options && options.basePxFontSize) || 16;
|
|
8
9
|
/**
|
|
9
|
-
* @description
|
|
10
|
-
* @param value token value
|
|
11
|
-
* @param unit unit string like px or value
|
|
12
|
-
* @returns boolean
|
|
13
|
-
*/
|
|
14
|
-
const hasUnit = (value, unit) => {
|
|
15
|
-
if (typeof value === 'number') {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
return value.indexOf(unit) > -1;
|
|
19
|
-
};
|
|
20
|
-
/**
|
|
21
|
-
* @description converts dimension tokens value to `rem`, ignores `em` as they are relative to the font size of the parent element
|
|
10
|
+
* @description converts dimension tokens value to an array with both `rem` and `px` values
|
|
22
11
|
* @type value transformer — [StyleDictionary.ValueTransform](https://github.com/amzn/style-dictionary/blob/main/types/Transform.d.ts)
|
|
23
12
|
* @matcher matches all tokens of $type `dimension`
|
|
24
13
|
* @transformer returns an array with the `rem` and `pixel` string
|
|
14
|
+
* @note Expects W3C DTCG format { value: number, unit: "px" | "rem" | "em" }
|
|
25
15
|
*/
|
|
26
16
|
export const dimensionToRemPxArray = {
|
|
27
17
|
name: 'dimension/remPxArray',
|
|
@@ -31,16 +21,25 @@ export const dimensionToRemPxArray = {
|
|
|
31
21
|
transform: (token, config, options) => {
|
|
32
22
|
const valueProp = options.usesDtcg ? '$value' : 'value';
|
|
33
23
|
const baseFont = getBasePxFontSize(config);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
24
|
+
try {
|
|
25
|
+
const { value, unit } = parseDimension(token[valueProp]);
|
|
26
|
+
if (value === 0) {
|
|
27
|
+
return ['0', '0'];
|
|
28
|
+
}
|
|
29
|
+
// em values pass through unchanged (relative to parent, cannot convert)
|
|
30
|
+
if (unit === 'em') {
|
|
31
|
+
return [`${value}em`, `${value}em`];
|
|
32
|
+
}
|
|
33
|
+
// rem values pass through, convert to px for second value
|
|
34
|
+
if (unit === 'rem') {
|
|
35
|
+
return [`${value}rem`, `${value * baseFont}px`];
|
|
36
|
+
}
|
|
37
|
+
// px values convert to rem for first value
|
|
38
|
+
return [`${value / baseFont}rem`, `${value}px`];
|
|
40
39
|
}
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
catch (error) {
|
|
41
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
42
|
+
throw new Error(`Invalid dimension token: '${token.name}: ${JSON.stringify(token[valueProp])}' is not valid and cannot be transformed to 'rem' - ${errorMessage}\n`);
|
|
43
43
|
}
|
|
44
|
-
return [`${floatVal / baseFont}rem`, `${floatVal}px`];
|
|
45
44
|
},
|
|
46
45
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Transform } from 'style-dictionary/types';
|
|
2
2
|
/**
|
|
3
|
-
* @description converts duration tokens
|
|
3
|
+
* @description converts duration tokens to css duration string
|
|
4
4
|
* @type value transformer — [StyleDictionary.ValueTransform](https://github.com/amzn/style-dictionary/blob/main/types/Transform.d.ts)
|
|
5
5
|
* @matcher matches all tokens of $type `duration`
|
|
6
6
|
* @transformer returns a css duration
|
|
7
|
+
* @note W3C DTCG format: { value: number, unit: "ms" | "s" }
|
|
7
8
|
*/
|
|
8
9
|
export declare const durationToCss: Transform;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { isDuration } from '../filters/index.js';
|
|
2
2
|
/**
|
|
3
|
-
* @description converts duration tokens
|
|
3
|
+
* @description converts duration tokens to css duration string
|
|
4
4
|
* @type value transformer — [StyleDictionary.ValueTransform](https://github.com/amzn/style-dictionary/blob/main/types/Transform.d.ts)
|
|
5
5
|
* @matcher matches all tokens of $type `duration`
|
|
6
6
|
* @transformer returns a css duration
|
|
7
|
+
* @note W3C DTCG format: { value: number, unit: "ms" | "s" }
|
|
7
8
|
*/
|
|
8
9
|
export const durationToCss = {
|
|
9
10
|
name: 'duration/css',
|
|
@@ -12,18 +13,24 @@ export const durationToCss = {
|
|
|
12
13
|
filter: isDuration,
|
|
13
14
|
transform: (token, _config, options) => {
|
|
14
15
|
const valueProp = options.usesDtcg ? '$value' : 'value';
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
const tokenValue = token[valueProp];
|
|
17
|
+
// Validate W3C DTCG object format: { value: number, unit: "ms" | "s" }
|
|
18
|
+
if (typeof tokenValue !== 'object' || tokenValue === null || !('value' in tokenValue) || !('unit' in tokenValue)) {
|
|
19
|
+
throw new Error(`duration token value must be an object with "value" and "unit" properties (W3C DTCG format). Invalid token: ${token.name} with value: ${JSON.stringify(tokenValue)}`);
|
|
18
20
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
const { value, unit } = tokenValue;
|
|
22
|
+
// Validate unit
|
|
23
|
+
if (unit !== 'ms' && unit !== 's') {
|
|
24
|
+
throw new Error(`duration token unit must be "ms" or "s", invalid token: ${token.name} with unit: ${unit}`);
|
|
25
|
+
}
|
|
26
|
+
// Validate value is a finite, non-negative number
|
|
27
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {
|
|
28
|
+
throw new Error(`duration token value must be a finite, non-negative number, invalid token: ${token.name} with value: ${value}`);
|
|
29
|
+
}
|
|
30
|
+
// Convert ms >= 1000 to seconds for cleaner output
|
|
31
|
+
if (unit === 'ms' && value >= 1000) {
|
|
32
|
+
return `${value / 1000}s`;
|
|
25
33
|
}
|
|
26
|
-
// return value
|
|
27
34
|
return `${value}${unit}`;
|
|
28
35
|
},
|
|
29
36
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { toHex } from 'color2k';
|
|
2
2
|
import { isGradient } from '../filters/isGradient.js';
|
|
3
3
|
import { getTokenValue } from './utilities/getTokenValue.js';
|
|
4
|
+
import { normalizeColorValue } from './utilities/normalizeColorValue.js';
|
|
4
5
|
/**
|
|
5
6
|
* @description converts gradient tokens value to css gradient
|
|
6
7
|
* @type value transformer — [StyleDictionary.ValueTransform](https://github.com/amzn/style-dictionary/blob/main/types/Transform.d.ts)
|
|
@@ -16,7 +17,7 @@ export const gradientToCss = {
|
|
|
16
17
|
var _a, _b;
|
|
17
18
|
const { angle } = (_b = (_a = token.$extensions) === null || _a === void 0 ? void 0 : _a['org.primer.gradient']) !== null && _b !== void 0 ? _b : {};
|
|
18
19
|
const stops = getTokenValue(token).map(({ color, position }) => {
|
|
19
|
-
return `${toHex(color)} ${position * 100}%`;
|
|
20
|
+
return `${toHex(normalizeColorValue(color))} ${position * 100}%`;
|
|
20
21
|
});
|
|
21
22
|
return `linear-gradient(${angle || 180}deg, ${stops.join(', ')})`;
|
|
22
23
|
},
|