0
# Utility Components
1
2
Additional UI components and utility functions that support accessibility, navigation, layout, and component development patterns within the @keystone-ui/core ecosystem.
3
4
## Capabilities
5
6
### VisuallyHidden Component
7
8
Accessibility component that hides content visually while keeping it available to screen readers and assistive technologies.
9
10
```typescript { .api }
11
/**
12
* Component for screen reader-only content
13
* @param props - Content and element type props
14
* @returns JSX element hidden visually but accessible to screen readers
15
*/
16
function VisuallyHidden(props: VisuallyHiddenProps): JSX.Element;
17
18
interface VisuallyHiddenProps {
19
/** Content to hide visually but keep accessible */
20
children?: ReactNode;
21
/** Element or component to render as */
22
as?: ElementType;
23
}
24
25
/**
26
* CSS styles object for creating visually hidden content
27
*/
28
const visuallyHiddenStyles: {
29
readonly border: 0;
30
readonly clip: 'rect(0, 0, 0, 0)';
31
readonly height: 1;
32
readonly overflow: 'hidden';
33
readonly padding: 0;
34
readonly position: 'absolute';
35
readonly whiteSpace: 'nowrap';
36
readonly width: 1;
37
};
38
```
39
40
**Usage Examples:**
41
42
```typescript
43
import { VisuallyHidden } from "@keystone-ui/core";
44
45
// Screen reader instructions
46
<button>
47
<IconEdit />
48
<VisuallyHidden>Edit user profile</VisuallyHidden>
49
</button>
50
51
// Form labels for icon-only inputs
52
<div>
53
<VisuallyHidden as="label" htmlFor="search">
54
Search products
55
</VisuallyHidden>
56
<input id="search" type="search" placeholder="Search..." />
57
</div>
58
59
// Skip navigation link
60
<VisuallyHidden as="a" href="#main-content">
61
Skip to main content
62
</VisuallyHidden>
63
64
// Custom element type
65
<VisuallyHidden as="h2">
66
Section heading for screen readers only
67
</VisuallyHidden>
68
```
69
70
### Link Component
71
72
Styled link component with theme-integrated colors and hover states.
73
74
```typescript { .api }
75
/**
76
* Themed link component with hover states
77
* @param props - Link styling and content props
78
* @returns JSX element with theme-based link styling
79
*/
80
function Link(props: LinkProps): JSX.Element;
81
82
interface LinkProps {
83
/** Element or component to render as */
84
as?: ElementType;
85
/** Child content */
86
children?: ReactNode;
87
/** All other HTML anchor attributes */
88
[key: string]: any;
89
}
90
```
91
92
**Usage Examples:**
93
94
```typescript
95
import { Link } from "@keystone-ui/core";
96
97
// Basic themed link
98
<Link href="/about">About Us</Link>
99
100
// Custom element (React Router Link)
101
<Link as={RouterLink} to="/dashboard">
102
Dashboard
103
</Link>
104
105
// External link
106
<Link href="https://keystonejs.com" target="_blank" rel="noopener">
107
KeystoneJS
108
</Link>
109
110
// Button-styled link
111
<Link
112
as="button"
113
onClick={handleClick}
114
style={{ background: 'none', border: 'none' }}
115
>
116
Click me
117
</Link>
118
```
119
120
### Divider Component
121
122
Visual separator component with horizontal or vertical orientation and theme-integrated styling.
123
124
```typescript { .api }
125
/**
126
* Visual separator with orientation support
127
* @param props - Divider styling and layout props
128
* @returns JSX element as visual separator
129
*/
130
function Divider(props: DividerProps): JSX.Element;
131
132
interface DividerProps extends MarginProps {
133
/** Divider color from theme palette */
134
color?: ResponsiveProp<keyof Theme['palette']>;
135
/** Divider orientation */
136
orientation?: 'horizontal' | 'vertical';
137
/** CSS class name */
138
className?: string;
139
/** No children allowed */
140
children?: never;
141
}
142
```
143
144
**Usage Examples:**
145
146
```typescript
147
import { Divider, Stack, Inline, Text } from "@keystone-ui/core";
148
149
// Horizontal divider (default)
150
<Stack gap="medium">
151
<Text>Section 1</Text>
152
<Divider />
153
<Text>Section 2</Text>
154
</Stack>
155
156
// Vertical divider
157
<Inline gap="medium" align="center">
158
<Text>Item 1</Text>
159
<Divider orientation="vertical" />
160
<Text>Item 2</Text>
161
</Inline>
162
163
// Custom colored divider with spacing
164
<Divider
165
color="blue300"
166
marginY="large"
167
orientation="horizontal"
168
/>
169
170
// Responsive divider color
171
<Divider color={["neutral300", "neutral400"]} />
172
```
173
174
### Center Component
175
176
Layout component for centering content both horizontally and vertically.
177
178
```typescript { .api }
179
/**
180
* Component for centering content horizontally and vertically
181
* @param props - Center layout and styling props
182
* @returns JSX element with centered content
183
*/
184
function Center(props: CenterProps): JSX.Element;
185
186
interface CenterProps extends BoxProps {
187
/** Fill entire viewport height and width */
188
fillView?: boolean;
189
}
190
```
191
192
**Usage Examples:**
193
194
```typescript
195
import { Center, Text, Stack } from "@keystone-ui/core";
196
197
// Center content in container
198
<Center height="200px" background="neutral100">
199
<Text>Centered content</Text>
200
</Center>
201
202
// Full viewport centering for loading states
203
<Center fillView>
204
<Stack gap="medium" align="center">
205
<Spinner />
206
<Text>Loading...</Text>
207
</Stack>
208
</Center>
209
210
// Center with additional styling
211
<Center
212
height="300px"
213
background="blue50"
214
rounding="large"
215
padding="xlarge"
216
>
217
<Text size="large" textAlign="center">
218
Welcome Message
219
</Text>
220
</Center>
221
```
222
223
## Utility Functions
224
225
### Polymorphic Component Helper
226
227
Utility function for creating components that support the `as` prop for element/component polymorphism.
228
229
```typescript { .api }
230
/**
231
* Creates a component with polymorphic `as` prop support
232
* @param render - Render function receiving props and ref
233
* @returns Component with polymorphic behavior
234
*/
235
function forwardRefWithAs<DefaultElementType extends ElementType, BaseProps>(
236
render: (
237
props: BaseProps & { as?: ElementType },
238
ref: React.Ref<any>
239
) => ReactNode
240
): CompWithAsProp<BaseProps, DefaultElementType>;
241
242
type CompWithAsProp<Props, DefaultElementType extends ElementType> = <
243
Comp extends ElementType = DefaultElementType
244
>(
245
props: AsProp<Comp, Props> & Props
246
) => ReactElement;
247
```
248
249
**Usage Examples:**
250
251
```typescript
252
import { forwardRefWithAs } from "@keystone-ui/core";
253
254
// Create custom polymorphic component
255
const MyButton = forwardRefWithAs<'button', { variant?: 'primary' | 'secondary' }>(
256
({ variant = 'primary', as: Tag = 'button', ...props }, ref) => {
257
return (
258
<Tag
259
ref={ref}
260
className={`btn btn-${variant}`}
261
{...props}
262
/>
263
);
264
}
265
);
266
267
// Usage with different elements
268
<MyButton>Default button</MyButton>
269
<MyButton as="a" href="/link">Link button</MyButton>
270
<MyButton as={CustomComponent}>Custom component</MyButton>
271
```
272
273
### Element Tag Helper
274
275
Utility function to determine appropriate child element based on parent element context.
276
277
```typescript { .api }
278
/**
279
* Returns appropriate child tag for a parent element
280
* @param parentTag - Parent element type
281
* @returns Appropriate child element tag
282
*/
283
function getChildTag(parentTag?: ElementType): ElementType;
284
```
285
286
**Usage Examples:**
287
288
```typescript
289
import { getChildTag } from "@keystone-ui/core";
290
291
// Used internally by Stack and Inline components
292
function MyListComponent({ as = 'div', children }) {
293
const ChildTag = getChildTag(as);
294
295
return (
296
<as>
297
{children.map(child => (
298
<ChildTag key={child.id}>
299
{child.content}
300
</ChildTag>
301
))}
302
</as>
303
);
304
}
305
306
// Returns 'li' for 'ul' or 'ol', 'div' for others
307
getChildTag('ul'); // 'li'
308
getChildTag('ol'); // 'li'
309
getChildTag('div'); // 'div'
310
```
311
312
### ID Generation Utilities
313
314
Utilities for generating unique IDs for accessibility and component relationships.
315
316
```typescript { .api }
317
/**
318
* Generates unique ID for components, with SSR support
319
* @param idFromProps - Optional ID provided via props
320
* @returns Unique string ID or undefined
321
*/
322
function useId(idFromProps?: string | null): string | undefined;
323
324
/**
325
* Creates compound ID from multiple inputs
326
* @param args - String, number, or null/undefined values to combine
327
* @returns Compound ID string with null/undefined values filtered out
328
*/
329
function makeId(...args: (string | number | null | undefined)[]): string;
330
```
331
332
**Usage Examples:**
333
334
```typescript
335
import { useId, makeId } from "@keystone-ui/core";
336
337
function FormField({ id: providedId, label, ...props }) {
338
// Generate ID if not provided
339
const id = useId(providedId);
340
const labelId = makeId(id, 'label');
341
const helpId = makeId(id, 'help');
342
343
return (
344
<div>
345
<label id={labelId} htmlFor={id}>
346
{label}
347
</label>
348
<input
349
id={id}
350
aria-labelledby={labelId}
351
aria-describedby={helpId}
352
{...props}
353
/>
354
<div id={helpId}>
355
Help text
356
</div>
357
</div>
358
);
359
}
360
361
// makeId filters out null/undefined values
362
makeId('field', null, 'label', undefined); // 'field--label'
363
makeId('modal', 123, 'content'); // 'modal--123--content'
364
```
365
366
### Development Warning Utility
367
368
Development-only warning utility for component validation and debugging.
369
370
```typescript { .api }
371
/**
372
* Logs warning to console in development mode only
373
* @param condition - When true, warning will be logged
374
* @param message - Warning message to display
375
*/
376
function devWarning(condition: boolean, message: string): void;
377
```
378
379
**Usage Examples:**
380
381
```typescript
382
import { devWarning } from "@keystone-ui/core";
383
384
function MyComponent({ value, onChange }) {
385
// Warn about missing onChange handler
386
devWarning(
387
value !== undefined && !onChange,
388
'MyComponent: controlled component is missing onChange handler'
389
);
390
391
// Warn about invalid prop combinations
392
devWarning(
393
variant === 'custom' && !customStyle,
394
'MyComponent: customStyle prop is required when variant="custom"'
395
);
396
397
return <div>{/* component content */}</div>;
398
}
399
```
400
401
### Responsive Prop Mapping
402
403
Utility function for mapping responsive prop values to theme scales, used internally by components for responsive design.
404
405
```typescript { .api }
406
/**
407
* Maps responsive prop values to theme scale values
408
* @param value - Single value or array of responsive values
409
* @param valueMap - Theme scale object to map values against
410
* @returns Mapped value(s) for CSS properties
411
*/
412
function mapResponsiveProp<
413
Map extends Record<string, string | number>,
414
Keys extends keyof Map
415
>(
416
value: Keys | readonly (Keys | null)[],
417
valueMap: Map
418
): string | number | (string | number | null)[];
419
```
420
421
**Usage Examples:**
422
423
```typescript
424
import { mapResponsiveProp, useTheme } from "@keystone-ui/core";
425
426
function CustomComponent({ padding, margin }) {
427
const theme = useTheme();
428
429
// Map single values
430
const paddingValue = mapResponsiveProp(padding, theme.spacing);
431
// padding = "medium" -> theme.spacing.medium (12)
432
433
// Map responsive arrays
434
const marginValue = mapResponsiveProp(
435
["small", "medium", "large"],
436
theme.spacing
437
);
438
// Returns [8, 12, 16]
439
440
// Handle null values in arrays
441
const responsiveValue = mapResponsiveProp(
442
["small", null, "large"],
443
theme.spacing
444
);
445
// Returns [8, null, 16]
446
447
return (
448
<div style={{
449
padding: paddingValue,
450
margin: marginValue
451
}}>
452
Content
453
</div>
454
);
455
}
456
457
// Used internally by Box component
458
const boxStyles = {
459
padding: mapResponsiveProp(paddingProp, theme.spacing),
460
backgroundColor: mapResponsiveProp(backgroundProp, theme.palette)
461
};
462
```
463
464
### Portal Component
465
466
Utility component for rendering content outside the normal component tree using React portals.
467
468
```typescript { .api }
469
/**
470
* Renders children in a portal to document.body
471
* @param props - Portal props with children
472
* @returns React portal or null for SSR compatibility
473
*/
474
function Portal(props: PortalProps): React.ReactPortal | null;
475
476
interface PortalProps {
477
children: ReactElement;
478
}
479
```
480
481
**Usage Examples:**
482
483
```typescript
484
import { Portal } from "@keystone-ui/core";
485
486
function Modal({ isOpen, children }) {
487
if (!isOpen) return null;
488
489
return (
490
<Portal>
491
<div className="modal-overlay">
492
<div className="modal-content">
493
{children}
494
</div>
495
</div>
496
</Portal>
497
);
498
}
499
500
function Tooltip({ content, children }) {
501
return (
502
<div>
503
{children}
504
<Portal>
505
<div className="tooltip">
506
{content}
507
</div>
508
</Portal>
509
</div>
510
);
511
}
512
```
513
514
## TypeScript Utilities
515
516
### Identity Type Helper
517
518
TypeScript utility for preserving generic type information while enabling type inference.
519
520
```typescript { .api }
521
/**
522
* TypeScript utility for preserving generic types with inference
523
* @returns Function that preserves and validates generic type
524
*/
525
function identityType<T>(): <U extends T>(u: U) => U;
526
```
527
528
**Usage Examples:**
529
530
```typescript
531
import { identityType } from "@keystone-ui/core";
532
533
// Define strongly-typed configuration objects
534
const createConfig = identityType<{ name: string; value: number }>()({
535
name: 'example',
536
value: 42
537
// TypeScript will enforce this shape
538
});
539
540
// Used internally for theme type safety
541
const toneConfig = identityType<Record<string, ToneColor>>()({
542
primary: { fill: '#blue', tint: '#lightblue' },
543
secondary: { fill: '#gray', tint: '#lightgray' }
544
});
545
```
546
547
All utility components and functions are designed to work seamlessly with the theme system and provide consistent behavior across the @keystone-ui/core ecosystem.