@skyscanner/backpack-web 41.13.0 → 41.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.
Files changed (31) hide show
  1. package/bpk-component-input/src/withOpenEvents.d.ts +17 -10
  2. package/bpk-component-input/src/withOpenEvents.js +13 -3
  3. package/bpk-component-layout/index.d.ts +18 -0
  4. package/bpk-component-layout/index.js +29 -0
  5. package/bpk-component-layout/src/BpkBox.d.ts +3 -0
  6. package/bpk-component-layout/src/BpkBox.js +35 -0
  7. package/bpk-component-layout/src/BpkFlex.d.ts +3 -0
  8. package/bpk-component-layout/src/BpkFlex.js +53 -0
  9. package/bpk-component-layout/src/BpkGrid.d.ts +3 -0
  10. package/bpk-component-layout/src/BpkGrid.js +59 -0
  11. package/bpk-component-layout/src/BpkGridItem.d.ts +3 -0
  12. package/bpk-component-layout/src/BpkGridItem.js +47 -0
  13. package/bpk-component-layout/src/BpkProvider.d.ts +14 -0
  14. package/bpk-component-layout/src/BpkProvider.js +42 -0
  15. package/bpk-component-layout/src/BpkStack.constant.d.ts +2 -0
  16. package/bpk-component-layout/src/BpkStack.constant.js +22 -0
  17. package/bpk-component-layout/src/BpkStack.d.ts +5 -0
  18. package/bpk-component-layout/src/BpkStack.js +61 -0
  19. package/bpk-component-layout/src/BpkVessel.d.ts +46 -0
  20. package/bpk-component-layout/src/BpkVessel.js +72 -0
  21. package/bpk-component-layout/src/commonProps.d.ts +86 -0
  22. package/bpk-component-layout/src/commonProps.js +1 -0
  23. package/bpk-component-layout/src/theme.d.ts +36 -0
  24. package/bpk-component-layout/src/theme.js +229 -0
  25. package/bpk-component-layout/src/tokenUtils.d.ts +108 -0
  26. package/bpk-component-layout/src/tokenUtils.js +323 -0
  27. package/bpk-component-layout/src/tokens.d.ts +96 -0
  28. package/bpk-component-layout/src/tokens.js +138 -0
  29. package/bpk-component-layout/src/types.d.ts +236 -0
  30. package/bpk-component-layout/src/types.js +1 -0
  31. package/package.json +2 -1
@@ -0,0 +1,323 @@
1
+ /*
2
+ * Backpack - Skyscanner's Design System
3
+ *
4
+ * Copyright 2016 Skyscanner Ltd
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+
19
+ import StackOptionKeys from "./BpkStack.constant";
20
+ import { getSpacingValue } from "./theme";
21
+ import { BpkBreakpointToChakraKey, isValidSpacingValue, isValidSizeValue, isValidPositionValue, isPercentage } from "./tokens";
22
+
23
+ /**
24
+ * Allowlisted, component-scoped prop groups that are eligible for Backpack responsive value
25
+ * processing (Backpack breakpoint keys -> Chakra breakpoint keys).
26
+ *
27
+ * NOTE:
28
+ * - Spacing/size/position props are processed separately via `processBpkProps` and therefore
29
+ * are intentionally NOT included here.
30
+ * - These groups are meant to keep the responsive surface predictable per component, while
31
+ * avoiding duplicated per-component breakpoint mapping logic.
32
+ */
33
+
34
+ export const BPK_RESPONSIVE_PROP_GROUPS_BY_COMPONENT = {
35
+ BpkBox: {
36
+ container: [
37
+ // Display
38
+ 'display',
39
+ // Flex container props
40
+ 'flexDirection', 'flexWrap', 'justifyContent', 'alignItems', 'alignContent',
41
+ // Grid container props
42
+ 'gridTemplateColumns', 'gridTemplateRows', 'gridTemplateAreas', 'gridAutoFlow', 'gridAutoRows', 'gridAutoColumns'],
43
+ item: [
44
+ // Flex item props
45
+ 'flex', 'flexGrow', 'flexShrink', 'flexBasis', 'order', 'alignSelf', 'justifySelf',
46
+ // Grid item placement props (useful on Box when composing grids)
47
+ 'gridColumn', 'gridRow']
48
+ },
49
+ // Note: BpkFlex maps its public API props to these Chakra keys.
50
+ BpkFlex: {
51
+ container: ['flexDirection', 'justifyContent', 'alignItems', 'flexWrap'],
52
+ item: ['flexGrow', 'flexShrink', 'flexBasis']
53
+ },
54
+ // Note: BpkGrid maps its public API props to these Chakra keys.
55
+ BpkGrid: {
56
+ container: ['justifyContent', 'alignItems', 'gridTemplateColumns', 'gridTemplateRows', 'gridTemplateAreas', 'gridAutoFlow', 'gridAutoRows', 'gridAutoColumns'],
57
+ item: [
58
+ // Used when placing the grid itself within a parent grid.
59
+ 'gridColumn', 'gridRow']
60
+ },
61
+ // Note: BpkStack uses Chakra Stack option prop names directly.
62
+ BpkStack: {
63
+ container: StackOptionKeys
64
+ }
65
+ };
66
+ export const BPK_RESPONSIVE_PROP_KEYS_BY_COMPONENT = {
67
+ BpkBox: [...BPK_RESPONSIVE_PROP_GROUPS_BY_COMPONENT.BpkBox.container, ...(BPK_RESPONSIVE_PROP_GROUPS_BY_COMPONENT.BpkBox.item ?? [])],
68
+ BpkFlex: [...BPK_RESPONSIVE_PROP_GROUPS_BY_COMPONENT.BpkFlex.container, ...(BPK_RESPONSIVE_PROP_GROUPS_BY_COMPONENT.BpkFlex.item ?? [])],
69
+ BpkGrid: [...BPK_RESPONSIVE_PROP_GROUPS_BY_COMPONENT.BpkGrid.container, ...(BPK_RESPONSIVE_PROP_GROUPS_BY_COMPONENT.BpkGrid.item ?? [])],
70
+ BpkStack: [...BPK_RESPONSIVE_PROP_GROUPS_BY_COMPONENT.BpkStack.container]
71
+ };
72
+ function filterToAllowlist(props, allowlist) {
73
+ const allowed = new Set(allowlist);
74
+ const result = {};
75
+ Object.keys(props).forEach(key => {
76
+ if (allowed.has(key) && props[key] !== undefined) {
77
+ result[key] = props[key];
78
+ }
79
+ });
80
+ return result;
81
+ }
82
+
83
+ /**
84
+ * Process a component's props in one place:
85
+ * - strip className/style
86
+ * - process spacing/size/position props (including breakpoint mapping + token conversion)
87
+ * - process allowlisted non-spacing responsive layout props (breakpoint mapping only)
88
+ *
89
+ * The allowlist is grouped by component via `BPK_RESPONSIVE_PROP_KEYS_BY_COMPONENT`.
90
+ *
91
+ * @param {T} props - The component props to process.
92
+ * @param {ProcessBpkComponentPropsOptions} options - Component processing options (allowlist group + mapping).
93
+ * @returns {Record<string, any>} The processed props ready to pass to Chakra primitives.
94
+ */
95
+ export function processBpkComponentProps(props, options) {
96
+ const processed = processBpkProps(props);
97
+ const allowlist = BPK_RESPONSIVE_PROP_KEYS_BY_COMPONENT[options.component];
98
+ const responsiveSource = options.responsiveProps ? filterToAllowlist(options.responsiveProps, allowlist) : filterToAllowlist(processed, allowlist);
99
+ if (Object.keys(responsiveSource).length === 0) {
100
+ return processed;
101
+ }
102
+
103
+ // Ensure allowlisted layout props do NOT fall through unprocessed (e.g. array responsive values).
104
+ // These props must be provided via the responsive processing pipeline only.
105
+ const cleanedProcessed = {
106
+ ...processed
107
+ };
108
+ allowlist.forEach(key => {
109
+ if (key in cleanedProcessed) {
110
+ delete cleanedProcessed[key];
111
+ }
112
+ });
113
+ const responsiveProcessed = processResponsiveProps(responsiveSource, options.propNameMap);
114
+
115
+ // Remove keys that ended up as `undefined` (e.g. array responsive values are rejected).
116
+ Object.keys(responsiveProcessed).forEach(key => {
117
+ if (responsiveProcessed[key] === undefined) {
118
+ delete responsiveProcessed[key];
119
+ }
120
+ });
121
+ return {
122
+ ...cleanedProcessed,
123
+ ...responsiveProcessed
124
+ };
125
+ }
126
+
127
+ /**
128
+ * Converts Backpack spacing token to Chakra UI compatible value
129
+ * Returns the actual spacing value from the theme, not a token path
130
+ *
131
+ * @param {string} value - Backpack spacing token (e.g., 'bpk-spacing-base') or percentage
132
+ * @returns {string} The actual spacing value in rem or the percentage string
133
+ */
134
+ export function convertBpkSpacingToChakra(value) {
135
+ if (isPercentage(value)) {
136
+ return value; // Percentages pass through
137
+ }
138
+
139
+ // Look up the actual spacing value from the theme
140
+ const spacingValue = getSpacingValue(value);
141
+ if (spacingValue !== undefined) {
142
+ return spacingValue;
143
+ }
144
+
145
+ // Fallback: if token not found, return the value as-is (will cause a warning)
146
+ if (process.env.NODE_ENV !== 'production') {
147
+ // eslint-disable-next-line no-console
148
+ console.warn(`Spacing token "${value}" not found in theme. Returning as-is.`);
149
+ }
150
+ return value;
151
+ }
152
+
153
+ /**
154
+ * Recursively processes responsive values (arrays or objects) to validate and convert tokens
155
+ *
156
+ * @param {*} value - The value to process (could be string, array, or object)
157
+ * @param {Function} converter - Function to convert valid tokens to actual values
158
+ * @param {Function} validator - Function to validate if a token is allowed
159
+ * @param {string} propName - The name of the prop being processed (for warning messages)
160
+ * @returns {*} The processed value with tokens converted, or undefined for invalid tokens
161
+ */
162
+ export function normalizeResponsiveObject(value) {
163
+ const normalized = {};
164
+ Object.entries(value).forEach(([key, val]) => {
165
+ if (key === 'base') {
166
+ normalized.base = val;
167
+ return;
168
+ }
169
+ const chakraKey = BpkBreakpointToChakraKey[key];
170
+ if (chakraKey) {
171
+ normalized[chakraKey] = val;
172
+ } else if (process.env.NODE_ENV !== 'production') {
173
+ // eslint-disable-next-line no-console
174
+ console.warn(`Unknown breakpoint "${key}" used in responsive prop. ` + 'Use Backpack breakpoint tokens such as mobile, tablet or desktop.');
175
+ }
176
+ });
177
+ return normalized;
178
+ }
179
+ export function processResponsiveValue(value, converter, validator, propName) {
180
+ if (value === undefined || value === null) {
181
+ return value;
182
+ }
183
+ if (Array.isArray(value)) {
184
+ if (process.env.NODE_ENV !== 'production') {
185
+ // eslint-disable-next-line no-console
186
+ console.warn(`Array-based responsive values are not supported for prop "${propName}". ` + `Please use Backpack breakpoint keys instead.`);
187
+ }
188
+ return undefined;
189
+ }
190
+ if (typeof value === 'object') {
191
+ const normalized = normalizeResponsiveObject(value);
192
+ const result = {};
193
+ Object.keys(normalized).forEach(key => {
194
+ const processedValue = processResponsiveValue(normalized[key], converter, validator, propName);
195
+ if (processedValue !== undefined) {
196
+ result[key] = processedValue;
197
+ }
198
+ });
199
+ return Object.keys(result).length > 0 ? result : undefined;
200
+ }
201
+ const strValue = String(value);
202
+ if (!validator(strValue)) {
203
+ if (process.env.NODE_ENV !== 'production') {
204
+ // eslint-disable-next-line no-console
205
+ console.warn(`Invalid value "${strValue}" for prop "${propName}". ` + `Only Backpack tokens are allowed.`);
206
+ }
207
+ return undefined; // Invalid values are removed
208
+ }
209
+ return converter(strValue);
210
+ }
211
+
212
+ /**
213
+ * Validates and converts spacing props for Chakra UI
214
+ * Handles all spacing-related properties including padding, margin, gap, size, border radius and position
215
+ *
216
+ * @param {T} props - Component props object
217
+ * @returns {Record<string, any>} Processed props with spacing tokens converted to actual values
218
+ */
219
+ export function processSpacingProps(props) {
220
+ const spacingKeys = [
221
+ // Padding props
222
+ 'padding', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft', 'paddingStart', 'paddingEnd', 'paddingInline',
223
+ // Margin props
224
+ 'margin', 'marginTop', 'marginRight', 'marginBottom', 'marginLeft', 'marginStart', 'marginEnd', 'marginInline',
225
+ // Gap and spacing
226
+ 'gap', 'spacing', 'rowGap', 'columnGap',
227
+ // Size props
228
+ 'width', 'height', 'minWidth', 'minHeight', 'maxWidth', 'maxHeight',
229
+ // Position props
230
+ 'top', 'right', 'bottom', 'left'];
231
+ const processed = {
232
+ ...props
233
+ };
234
+ const sizeKeys = ['width', 'height', 'minWidth', 'minHeight', 'maxWidth', 'maxHeight'];
235
+ const positionKeys = ['top', 'right', 'bottom', 'left'];
236
+ spacingKeys.forEach(key => {
237
+ if (key in processed && processed[key] !== undefined) {
238
+ const isSizeProp = sizeKeys.includes(key);
239
+ const isPositionProp = positionKeys.includes(key);
240
+ let converter;
241
+ if (isSizeProp || isPositionProp) {
242
+ converter = v => v;
243
+ } else {
244
+ converter = convertBpkSpacingToChakra;
245
+ }
246
+ let validator;
247
+ if (isSizeProp) {
248
+ validator = isValidSizeValue;
249
+ } else if (isPositionProp) {
250
+ validator = isValidPositionValue;
251
+ } else {
252
+ validator = isValidSpacingValue;
253
+ }
254
+ const processedValue = processResponsiveValue(processed[key], converter, validator, key);
255
+ if (processedValue !== undefined) {
256
+ processed[key] = processedValue;
257
+ } else {
258
+ delete processed[key];
259
+ }
260
+ }
261
+ });
262
+ return processed;
263
+ }
264
+
265
+ /**
266
+ * Processes all props to convert Backpack tokens to Chakra UI format
267
+ * Also explicitly removes className and style to prevent ad-hoc overrides
268
+ *
269
+ * Processing order:
270
+ * 1. Remove className & style
271
+ * 2. Process spacing props (includes position)
272
+ *
273
+ * @param {T} props - Component props object
274
+ * @returns {Record<string, any>} Processed props with tokens converted and disallowed props removed
275
+ */
276
+ export function processBpkProps(props) {
277
+ // Explicitly remove className and style to prevent style overrides
278
+ const {
279
+ className,
280
+ style,
281
+ ...cleanProps
282
+ } = props;
283
+ if (className !== undefined && process.env.NODE_ENV !== 'production') {
284
+ // eslint-disable-next-line no-console
285
+ console.warn('className prop is not allowed on Backpack layout components. ' + 'It has been removed to maintain design system consistency.');
286
+ }
287
+ if (style !== undefined && process.env.NODE_ENV !== 'production') {
288
+ // eslint-disable-next-line no-console
289
+ console.warn('style prop is not allowed on Backpack layout components. ' + 'It has been removed to maintain design system consistency.');
290
+ }
291
+
292
+ // Process spacing props (includes position)
293
+ return processSpacingProps(cleanProps);
294
+ }
295
+
296
+ /**
297
+ * Processes responsive props that are simple string/enum values (non-spacing)
298
+ * using Backpack breakpoint keys. Array syntax is rejected as in spacing.
299
+ *
300
+ * @param {*} value - The value to process
301
+ * @param {string} propName - The name of the prop being processed
302
+ * @returns {*} The processed value with breakpoint keys mapped to Chakra keys
303
+ */
304
+ export function processResponsiveStringProp(value, propName) {
305
+ return processResponsiveValue(value, v => v, () => true, propName);
306
+ }
307
+
308
+ /**
309
+ * Processes a collection of responsive props.
310
+ * @param {Record<string, any>} props - Object containing prop values.
311
+ * @param {Record<string, string>} propNameMap - Map of prop name to CSS/Chakra property name (for error messages and mapping).
312
+ * @returns {Record<string, any>} Processed props object.
313
+ */
314
+ export function processResponsiveProps(props, propNameMap) {
315
+ const processed = {};
316
+ Object.keys(props).forEach(key => {
317
+ if (props[key] !== undefined) {
318
+ const targetPropName = propNameMap ? propNameMap[key] || key : key;
319
+ processed[targetPropName] = processResponsiveStringProp(props[key], targetPropName);
320
+ }
321
+ });
322
+ return processed;
323
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Backpack Design Tokens for Layout Components
3
+ *
4
+ * This file provides token mappings from Backpack design tokens to Chakra UI theme.
5
+ * All tokens are sourced from @skyscanner/bpk-foundations-web
6
+ */
7
+ /**
8
+ * Backpack Spacing Tokens
9
+ * Use these constants to ensure type safety when passing spacing props
10
+ */
11
+ export declare const BpkSpacing: {
12
+ readonly None: "bpk-spacing-none";
13
+ readonly XS: "bpk-spacing-xs";
14
+ readonly SM: "bpk-spacing-sm";
15
+ readonly Base: "bpk-spacing-base";
16
+ readonly MD: "bpk-spacing-md";
17
+ readonly LG: "bpk-spacing-lg";
18
+ readonly XL: "bpk-spacing-xl";
19
+ readonly XXL: "bpk-spacing-xxl";
20
+ };
21
+ export type BpkSpacingToken = typeof BpkSpacing[keyof typeof BpkSpacing];
22
+ /**
23
+ * Backpack Breakpoint Tokens
24
+ * Use these constants to ensure type safety when defining responsive overrides
25
+ * These map to the simplified keys defined in the Chakra theme
26
+ */
27
+ export declare const BpkBreakpoint: {
28
+ readonly SmallMobile: "small-mobile";
29
+ readonly Mobile: "mobile";
30
+ readonly SmallTablet: "small-tablet";
31
+ readonly Tablet: "tablet";
32
+ readonly Desktop: "desktop";
33
+ };
34
+ export type BpkBreakpointToken = typeof BpkBreakpoint[keyof typeof BpkBreakpoint];
35
+ export type ChakraBreakpointKey = 'base' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
36
+ export declare const BpkBreakpointToChakraKey: Record<BpkBreakpointToken, ChakraBreakpointKey>;
37
+ /**
38
+ * Helper type for values that can be Backpack tokens or percentages
39
+ * but NOT px/rem values
40
+ */
41
+ export type BpkSpacingValue = BpkSpacingToken | `${number}%`;
42
+ export type BpkBreakpointValue = BpkBreakpointToken;
43
+ /**
44
+ * Helper type for size props that can use rem, percentages or semantic values.
45
+ * This is intentionally separate from BpkSpacingValue to avoid encouraging
46
+ * spacing tokens for explicit sizes.
47
+ */
48
+ export type BpkSizeValue = `${number}rem` | `${number}%` | 'auto' | 'full' | 'fit-content';
49
+ /**
50
+ * Helper type for position props that can use rem or percentages.
51
+ * We intentionally do not allow semantic values like 'auto' here.
52
+ */
53
+ export type BpkPositionValue = `${number}rem` | `${number}%`;
54
+ /**
55
+ * Helper type for flex-basis prop that can use rem, percentages or semantic values.
56
+ * Excludes 'px' values to enforce design system constraints.
57
+ */
58
+ export type BpkBasisValue = `${number}rem` | `${number}%` | 'auto' | 'content' | 'fit-content' | 'max-content' | 'min-content' | 'initial' | 'inherit';
59
+ /**
60
+ * Helper type for responsive values based on Backpack breakpoints.
61
+ *
62
+ * We intentionally only support:
63
+ * - a single scalar value (non-responsive)
64
+ * - an object keyed by Backpack breakpoint tokens (and optional base)
65
+ *
66
+ * We do NOT support array-based responsive values in the public API.
67
+ */
68
+ export type BpkResponsiveValue<T> = T | Partial<Record<BpkBreakpointToken | 'base', T>>;
69
+ /**
70
+ * Validates if a value is a percentage string
71
+ *
72
+ * @param {string} value - The value to validate
73
+ * @returns {boolean} True if the value is a valid percentage string
74
+ */
75
+ export declare function isPercentage(value: string): boolean;
76
+ /**
77
+ * Validates if a spacing value is valid (token or percentage)
78
+ *
79
+ * @param {string} value - The spacing value to validate
80
+ * @returns {boolean} True if the value is a valid Backpack spacing token or percentage
81
+ */
82
+ export declare function isValidSpacingValue(value: string): boolean;
83
+ /**
84
+ * Validates if a size value is valid
85
+ *
86
+ * @param {string} value - The size value to validate
87
+ * @returns {boolean} True if the value is a valid rem/percentage/semantic size
88
+ */
89
+ export declare function isValidSizeValue(value: string): boolean;
90
+ /**
91
+ * Validates if a position value is valid
92
+ *
93
+ * @param {string} value - The position value to validate
94
+ * @returns {boolean} True if the value is a valid rem or percentage
95
+ */
96
+ export declare function isValidPositionValue(value: string): boolean;
@@ -0,0 +1,138 @@
1
+ /*
2
+ * Backpack - Skyscanner's Design System
3
+ *
4
+ * Copyright 2016 Skyscanner Ltd
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+
19
+ /**
20
+ * Backpack Design Tokens for Layout Components
21
+ *
22
+ * This file provides token mappings from Backpack design tokens to Chakra UI theme.
23
+ * All tokens are sourced from @skyscanner/bpk-foundations-web
24
+ */
25
+
26
+ /**
27
+ * Backpack Spacing Tokens
28
+ * Use these constants to ensure type safety when passing spacing props
29
+ */
30
+ export const BpkSpacing = {
31
+ None: 'bpk-spacing-none',
32
+ XS: 'bpk-spacing-xs',
33
+ SM: 'bpk-spacing-sm',
34
+ Base: 'bpk-spacing-base',
35
+ MD: 'bpk-spacing-md',
36
+ LG: 'bpk-spacing-lg',
37
+ XL: 'bpk-spacing-xl',
38
+ XXL: 'bpk-spacing-xxl'
39
+ };
40
+ /**
41
+ * Backpack Breakpoint Tokens
42
+ * Use these constants to ensure type safety when defining responsive overrides
43
+ * These map to the simplified keys defined in the Chakra theme
44
+ */
45
+ export const BpkBreakpoint = {
46
+ SmallMobile: 'small-mobile',
47
+ Mobile: 'mobile',
48
+ SmallTablet: 'small-tablet',
49
+ Tablet: 'tablet',
50
+ Desktop: 'desktop'
51
+ };
52
+ export const BpkBreakpointToChakraKey = {
53
+ // Keep this mapping in sync with the breakpoints configured in `theme.ts`.
54
+ // `base` is reserved for "default value" and is not a breakpoint token.
55
+ 'small-mobile': 'sm',
56
+ mobile: 'md',
57
+ 'small-tablet': 'lg',
58
+ tablet: 'xl',
59
+ desktop: '2xl'
60
+ };
61
+
62
+ /**
63
+ * Helper type for values that can be Backpack tokens or percentages
64
+ * but NOT px/rem values
65
+ */
66
+
67
+ /**
68
+ * Helper type for size props that can use rem, percentages or semantic values.
69
+ * This is intentionally separate from BpkSpacingValue to avoid encouraging
70
+ * spacing tokens for explicit sizes.
71
+ */
72
+
73
+ /**
74
+ * Helper type for position props that can use rem or percentages.
75
+ * We intentionally do not allow semantic values like 'auto' here.
76
+ */
77
+
78
+ /**
79
+ * Helper type for flex-basis prop that can use rem, percentages or semantic values.
80
+ * Excludes 'px' values to enforce design system constraints.
81
+ */
82
+
83
+ /**
84
+ * Helper type for responsive values based on Backpack breakpoints.
85
+ *
86
+ * We intentionally only support:
87
+ * - a single scalar value (non-responsive)
88
+ * - an object keyed by Backpack breakpoint tokens (and optional base)
89
+ *
90
+ * We do NOT support array-based responsive values in the public API.
91
+ */
92
+
93
+ /**
94
+ * Validates if a value is a percentage string
95
+ *
96
+ * @param {string} value - The value to validate
97
+ * @returns {boolean} True if the value is a valid percentage string
98
+ */
99
+ export function isPercentage(value) {
100
+ return /^\d+(\.\d+)?%$/.test(value);
101
+ }
102
+
103
+ /**
104
+ * Validates if a spacing value is valid (token or percentage)
105
+ *
106
+ * @param {string} value - The spacing value to validate
107
+ * @returns {boolean} True if the value is a valid Backpack spacing token or percentage
108
+ */
109
+ export function isValidSpacingValue(value) {
110
+ return Object.values(BpkSpacing).includes(value) || isPercentage(value);
111
+ }
112
+
113
+ /**
114
+ * Validates if a size value is valid
115
+ *
116
+ * @param {string} value - The size value to validate
117
+ * @returns {boolean} True if the value is a valid rem/percentage/semantic size
118
+ */
119
+ export function isValidSizeValue(value) {
120
+ return /^-?\d+(\.\d+)?rem$/.test(value) ||
121
+ // rem values
122
+ isPercentage(value) ||
123
+ // percentage values
124
+ value === 'auto' || value === 'full' || value === 'fit-content';
125
+ }
126
+
127
+ /**
128
+ * Validates if a position value is valid
129
+ *
130
+ * @param {string} value - The position value to validate
131
+ * @returns {boolean} True if the value is a valid rem or percentage
132
+ */
133
+ export function isValidPositionValue(value) {
134
+ return /^-?\d+(\.\d+)?rem$/.test(value) ||
135
+ // rem values
136
+ isPercentage(value) // percentage values
137
+ ;
138
+ }