@sanity/ui 5.0.0-alpha.1 → 5.0.0-alpha.11

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 (93) hide show
  1. package/README.md +1 -1
  2. package/dist/LazyRefractor-CQ22IeXB.js +19 -0
  3. package/dist/LazyRefractor-CQ22IeXB.js.map +1 -0
  4. package/dist/cli/install.js +2 -1
  5. package/dist/index.d.ts +662 -796
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +2081 -1758
  8. package/dist/index.js.map +1 -1
  9. package/dist/polyfills.d.ts +1 -0
  10. package/dist/polyfills.js +5 -0
  11. package/dist/polyfills.js.map +1 -0
  12. package/dist/styles.css +1 -1
  13. package/package.json +16 -9
  14. package/src/cli/install.ts +2 -1
  15. package/src/components/badge/Badge.tsx +53 -0
  16. package/src/components/badge/badge.css +6 -0
  17. package/src/components/badge/badge.props.ts +31 -0
  18. package/src/components/box/box.css +5 -0
  19. package/src/components/box/box.props.ts +7 -2
  20. package/src/components/button/button.props.ts +27 -9
  21. package/src/components/card/card.props.ts +6 -2
  22. package/src/components/checkbox/Checkbox.tsx +4 -3
  23. package/src/components/checkbox/checkbox.props.ts +11 -3
  24. package/src/components/code/code.props.ts +10 -3
  25. package/src/components/container/Container.tsx +5 -1
  26. package/src/components/container/container.css +4 -0
  27. package/src/components/container/container.props.ts +6 -2
  28. package/src/components/dialog/Dialog.tsx +139 -0
  29. package/src/components/dialog/dialog.css +49 -0
  30. package/src/components/dialog/dialog.props.ts +45 -0
  31. package/src/components/eyebrow/Eyebrow.tsx +1 -1
  32. package/src/components/eyebrow/eyebrow.props.ts +6 -2
  33. package/src/components/flex/flex.css +5 -0
  34. package/src/components/flex/flex.props.ts +6 -2
  35. package/src/components/grid/grid.css +5 -0
  36. package/src/components/grid/grid.props.ts +6 -2
  37. package/src/components/h-stack/HStack.tsx +8 -14
  38. package/src/components/h-stack/hStack.props.ts +5 -13
  39. package/src/components/heading/Heading.tsx +1 -1
  40. package/src/components/heading/heading.props.ts +8 -2
  41. package/src/components/icon/icon.props.ts +6 -2
  42. package/src/components/icon-button/iconButton.props.ts +7 -2
  43. package/src/components/index.css +4 -0
  44. package/src/components/indicator/indicator.props.ts +6 -2
  45. package/src/components/indicator-stack/indicatorStack.props.ts +3 -1
  46. package/src/components/inline/inline.props.ts +6 -2
  47. package/src/components/label/label.props.ts +7 -1
  48. package/src/components/link/link.props.ts +4 -4
  49. package/src/components/list/list.props.ts +27 -9
  50. package/src/components/popover/Popover.tsx +85 -0
  51. package/src/components/popover/popover.css +21 -0
  52. package/src/components/popover/popover.props.ts +38 -0
  53. package/src/components/press-area/pressArea.props.ts +3 -1
  54. package/src/components/radio/Radio.tsx +1 -1
  55. package/src/components/radio/radio.props.ts +7 -2
  56. package/src/components/select/Select.tsx +47 -0
  57. package/src/components/select/select.css +67 -0
  58. package/src/components/select/select.props.ts +25 -0
  59. package/src/components/skip-to-content/skipToContent.props.ts +9 -3
  60. package/src/components/spinner/Spinner.tsx +1 -1
  61. package/src/components/spinner/spinner.props.ts +3 -0
  62. package/src/components/switch/Switch.tsx +1 -1
  63. package/src/components/switch/switch.props.ts +7 -2
  64. package/src/components/text/Text.tsx +1 -1
  65. package/src/components/text/text.props.ts +6 -2
  66. package/src/components/tooltip/Tooltip.tsx +59 -55
  67. package/src/components/tooltip/tooltip.css +19 -40
  68. package/src/components/tooltip/tooltip.props.ts +24 -8
  69. package/src/components/tooltip-group/TooltipGroup.tsx +2 -5
  70. package/src/components/tooltip-group/tooltipGroup.props.ts +3 -1
  71. package/src/components/v-stack/VStack.tsx +8 -14
  72. package/src/components/v-stack/vStack.props.ts +5 -13
  73. package/src/components/visually-hidden/visuallyHidden.props.ts +6 -2
  74. package/src/css/classes/generic/display.css +8 -8
  75. package/src/css/classes/generic/placement.css +31 -0
  76. package/src/css/classes/local/animation.css +4 -0
  77. package/src/css/classes/local/index.css +2 -0
  78. package/src/css/classes/local/top-anchor.css +4 -0
  79. package/src/css/classes/system/shadow.css +6 -6
  80. package/src/css/global/reset.css +32 -19
  81. package/src/css/tokens/semantic.css +4 -0
  82. package/src/css/tokens/surface.css +2 -0
  83. package/src/hooks/useIsClient.ts +10 -0
  84. package/src/index.ts +8 -0
  85. package/src/polyfills.ts +17 -0
  86. package/src/props/formElement.ts +34 -0
  87. package/src/types/FormElement.ts +2 -0
  88. package/src/utils/getProps.ts +1 -1
  89. package/src/utils/mergeTriggerProps.ts +66 -0
  90. package/src/utils/renderPortal.tsx +14 -0
  91. package/src/version.ts +2 -2
  92. package/dist/_chunks-es/LazyRefractor.js +0 -20
  93. package/dist/_chunks-es/LazyRefractor.js.map +0 -1
@@ -1,3 +1,8 @@
1
+ .sui-Grid {
2
+ min-width: 0;
3
+ min-height: 0;
4
+ }
5
+
1
6
  .sui-Grid[class*='sui-tone'] {
2
7
  --grid-background: var(--background-low);
3
8
  background-color: var(--grid-background);
@@ -8,9 +8,13 @@ import type {Responsive} from '../../types/Responsive'
8
8
  /** @public */
9
9
  export interface GridProps<T extends React.ElementType = 'div'>
10
10
  extends GridParentProps, GapProps, LayoutProps {
11
- /** Element to render */
11
+ /**
12
+ * HTML element or component to render.
13
+ */
12
14
  as?: T
13
- /** CSS **display** property */
15
+ /**
16
+ * CSS `display` property.
17
+ */
14
18
  display?: Responsive<DisplayGrid>
15
19
  }
16
20
 
@@ -1,33 +1,27 @@
1
1
  import clsx from 'clsx'
2
2
  import type {ComponentPropsWithRef, ElementType} from 'react'
3
3
 
4
- import {getProps} from '../../utils/getProps'
4
+ import {type ComponentProps} from '../../utils/getProps'
5
5
  import {suffixClassName} from '../../utils/suffixClassName'
6
6
  import {Flex} from '../flex/Flex'
7
- import {type HStackProps, hStackProps} from './hStack.props'
7
+ import {type HStackProps} from './hStack.props'
8
8
 
9
9
  const hStackClassName = suffixClassName('sui-HStack')
10
10
 
11
11
  /** @public */
12
- export function HStack<T extends ElementType = 'div'>(
13
- props: HStackProps<T> & Omit<ComponentPropsWithRef<T>, keyof HStackProps<T>>,
14
- ) {
15
- const {as, children, className, style, ...rest} = getProps(props, hStackProps)
16
- const Component = as || 'div'
17
-
12
+ export function HStack<T extends ElementType = 'div'>({
13
+ className,
14
+ ...props
15
+ }: HStackProps<T> & Omit<ComponentPropsWithRef<T>, keyof HStackProps<T>>) {
18
16
  return (
19
17
  <Flex
20
- as={Component}
21
18
  className={clsx(hStackClassName, className)}
22
- style={style}
23
19
  data-ui="HStack"
24
20
  display="inline-flex"
25
21
  alignItems="center"
26
22
  flexWrap="wrap"
27
- {...rest}
28
- >
29
- {children}
30
- </Flex>
23
+ {...(props as ComponentProps)}
24
+ />
31
25
  )
32
26
  }
33
27
 
@@ -1,15 +1,7 @@
1
- import {type GapProps, gapProps} from '../../props/gap'
2
- import {type PropDef} from '../../types/PropDef'
1
+ import type {FlexProps} from '../flex/flex.props'
3
2
 
4
3
  /** @public */
5
- export interface HStackProps<T extends React.ElementType = 'div'> extends Pick<GapProps, 'gap'> {
6
- /** Element to render */
7
- as?: T
8
- }
9
-
10
- export const hStackProps: Record<string, PropDef> = {
11
- as: {
12
- type: 'string',
13
- },
14
- gap: gapProps['gap'] as PropDef,
15
- }
4
+ export interface HStackProps<T extends React.ElementType = 'div'> extends Pick<
5
+ FlexProps<T>,
6
+ 'as' | 'gap'
7
+ > {}
@@ -41,7 +41,7 @@ export function Heading<T extends ElementType = 'h2'>({
41
41
 
42
42
  return (
43
43
  <Component
44
- className={clsx(headingClassName, 'sui-overflow-hidden', className)}
44
+ className={clsx(headingClassName, props.truncate && 'sui-overflow-hidden', className)}
45
45
  style={style}
46
46
  data-ui="Heading"
47
47
  {...rest}
@@ -5,9 +5,15 @@ import type {Responsive} from '../../types/Responsive'
5
5
 
6
6
  /** @public */
7
7
  export interface HeadingProps extends TypographyProps {
8
- /** Element to render */
8
+ /**
9
+ * Semantic heading element to render.
10
+ * @remarks Always set this explicitly.
11
+ */
9
12
  as?: HeadingTag
10
- /** CSS **font-size** property */
13
+ /**
14
+ * Sets font size.
15
+ * @remarks Independent of `as`.
16
+ */
11
17
  size?: Responsive<HeadingSize>
12
18
  }
13
19
 
@@ -10,9 +10,13 @@ import type {Responsive} from '../../types/Responsive'
10
10
  /** @public */
11
11
  export interface IconProps
12
12
  extends ComponentProps<'svg'>, Pick<TypographyProps, 'muted'>, MarginProps, ToneProps {
13
- /** Icon to render */
13
+ /**
14
+ * The icon component to render.
15
+ */
14
16
  icon: React.ComponentType<SVGProps<SVGSVGElement>>
15
- /** CSS **font-size** property */
17
+ /**
18
+ * Sets icon size.
19
+ */
16
20
  size?: Responsive<IconSize>
17
21
  }
18
22
 
@@ -9,9 +9,14 @@ export interface IconButtonProps<T extends React.ElementType = 'button'> extends
9
9
  ButtonProps<T>,
10
10
  'as' | 'density' | 'level' | 'loading' | 'tone'
11
11
  > {
12
- /** Button label */
12
+ /**
13
+ * Accessible name for the button.
14
+ * @remarks Because IconButtons have no visible text, this is required.
15
+ */
13
16
  'aria-label': string
14
- /** Icon */
17
+ /**
18
+ * Icon to render.
19
+ */
15
20
  'icon': React.ComponentType<SVGProps<SVGSVGElement>>
16
21
  }
17
22
 
@@ -1,9 +1,11 @@
1
+ @import './badge/badge.css';
1
2
  @import './box/box.css';
2
3
  @import './button/button.css';
3
4
  @import './card/card.css';
4
5
  @import './checkbox/checkbox.css';
5
6
  @import './code/code.css';
6
7
  @import './container/container.css';
8
+ @import './dialog/dialog.css';
7
9
  @import './eyebrow/eyebrow.css';
8
10
  @import './flex/flex.css';
9
11
  @import './grid/grid.css';
@@ -15,8 +17,10 @@
15
17
  @import './label/label.css';
16
18
  @import './link/link.css';
17
19
  @import './list/list.css';
20
+ @import './popover/popover.css';
18
21
  @import './press-area/press-area.css';
19
22
  @import './radio/radio.css';
23
+ @import './select/select.css';
20
24
  @import './spinner/spinner.css';
21
25
  @import './skip-to-content/skip-to-content.css';
22
26
  @import './switch/switch.css';
@@ -3,9 +3,13 @@ import {type PropDef} from '../../types/PropDef'
3
3
 
4
4
  /** @beta */
5
5
  export interface IndicatorProps<T extends React.ElementType = 'span'> extends ToneProps {
6
- /** Element to render */
6
+ /**
7
+ * HTML element to render.
8
+ */
7
9
  as?: T
8
- /** Label for aria-label attribute */
10
+ /**
11
+ * Accessible label.
12
+ */
9
13
  label?: string
10
14
  }
11
15
 
@@ -2,7 +2,9 @@ import {type PropDef} from '../../types/PropDef'
2
2
 
3
3
  /** @public */
4
4
  export interface IndicatorStackProps<T extends React.ElementType = 'div'> {
5
- /** Element to render */
5
+ /**
6
+ * HTML element to render.
7
+ */
6
8
  as?: T
7
9
  }
8
10
 
@@ -7,9 +7,13 @@ import {SPACE, type SpaceInherit} from '../../types/Space'
7
7
  /** @deprecated Use HStack component instead */
8
8
  /** @public */
9
9
  export interface InlineProps<T extends React.ElementType = 'div'> extends PaddingProps {
10
- /** Element to render */
10
+ /**
11
+ * HTML element or component to render.
12
+ */
11
13
  as?: T
12
- /** CSS **gap** property */
14
+ /**
15
+ * Space between children.
16
+ */
13
17
  gap?: Responsive<SpaceInherit>
14
18
  }
15
19
 
@@ -3,8 +3,14 @@ import {type PropDef} from '../../types/PropDef'
3
3
 
4
4
  /** @beta */
5
5
  export interface LabelProps extends React.ComponentProps<'label'>, MarginProps {
6
- /** Element to render */
6
+ /**
7
+ * Renders the label in a muted color.
8
+ */
7
9
  disabled?: boolean
10
+ /**
11
+ * Renders the label in the error color.
12
+ * @remarks Use when the linked input has a validation error.
13
+ */
8
14
  error?: boolean
9
15
  }
10
16
 
@@ -2,12 +2,12 @@ import {type PropDef} from '../../types/PropDef'
2
2
 
3
3
  /** @beta */
4
4
  export interface LinkProps extends React.ComponentProps<'a'> {
5
- /** If true, sets `target="_blank"` and `rel="noopener noreferrer"` */
5
+ /**
6
+ * Sets the link to `target="_blank"` and `rel="noopener noreferrer"`.
7
+ */
6
8
  openInNewTab?: boolean
7
9
  /**
8
- * If true, sets `text-decoration: underline`
9
- *
10
- * @defaultValue true
10
+ * Underlines the link.
11
11
  */
12
12
  underlined?: boolean
13
13
  }
@@ -11,7 +11,9 @@ import type {Responsive} from '../../types/Responsive'
11
11
 
12
12
  /** @beta */
13
13
  export interface ListProps<T extends ListTag = 'ul'> extends Pick<GapProps, 'gap'> {
14
- /** Element to render */
14
+ /**
15
+ * HTML element to render.
16
+ */
15
17
  as?: T
16
18
  }
17
19
 
@@ -24,11 +26,17 @@ export const listProps: Record<string, PropDef> = {
24
26
 
25
27
  /** @beta */
26
28
  export interface ListItemProps extends React.ComponentProps<'li'> {
27
- /** Composite prop for setting padding and gap */
29
+ /**
30
+ * Sets horizontal padding, gap, and minimum row height together.
31
+ */
28
32
  density?: Responsive<Density>
29
- /** Starting slot */
33
+ /**
34
+ * Slot at the left edge of the row.
35
+ */
30
36
  start?: React.ReactNode
31
- /** Ending slot */
37
+ /**
38
+ * Slot at the right edge of the row.
39
+ */
32
40
  end?: React.ReactNode
33
41
  }
34
42
 
@@ -73,9 +81,13 @@ export const listItemProps: Record<string, PropDef> = {
73
81
 
74
82
  /** @beta */
75
83
  export interface ListButtonItemProps<T extends React.ElementType = 'button'> extends ListItemProps {
76
- /** Element to render */
84
+ /**
85
+ * Element or component to render for the press target.
86
+ */
77
87
  as?: InteractiveAs<T>
78
- /** Selected state */
88
+ /**
89
+ * Marks the row as the current selection.
90
+ */
79
91
  selected?: boolean
80
92
  }
81
93
 
@@ -91,11 +103,17 @@ export const listButtonItemProps: Record<string, PropDef> = {
91
103
 
92
104
  /** @beta */
93
105
  export interface ListItemTextProps<T extends React.ElementType = 'div'> {
94
- /** Element to render */
106
+ /**
107
+ * HTML element to render.
108
+ */
95
109
  as?: T
96
- /** Title text */
110
+ /**
111
+ * Main label.
112
+ */
97
113
  title?: React.ReactNode
98
- /** Subtitle text */
114
+ /**
115
+ * Text below the title.
116
+ */
99
117
  subtitle?: React.ReactNode
100
118
  }
101
119
 
@@ -0,0 +1,85 @@
1
+ import clsx from 'clsx'
2
+ import {Activity, cloneElement, useId, useState, type ToggleEvent} from 'react'
3
+
4
+ import {useIsClient} from '../../hooks/useIsClient'
5
+ import {getProps} from '../../utils/getProps'
6
+ import {mergeTriggerProps} from '../../utils/mergeTriggerProps'
7
+ import {renderPortal} from '../../utils/renderPortal'
8
+ import {suffixClassName} from '../../utils/suffixClassName'
9
+ import {type PopoverProps, popoverProps} from './popover.props'
10
+
11
+ const popoverClassName = suffixClassName('sui-PopoverContent')
12
+
13
+ function PopoverRoot({
14
+ placement = 'bottom',
15
+ ...props
16
+ }: PopoverProps & {
17
+ triggerProps?: Record<string, unknown>
18
+ }) {
19
+ const {
20
+ children,
21
+ className,
22
+ style,
23
+ id: idProp,
24
+ anchorName,
25
+ content,
26
+ portal,
27
+ triggerProps: forwardedTriggerProps,
28
+ ...rest
29
+ } = getProps({placement, ...props}, popoverProps)
30
+ const reactId = useId()
31
+ const id = idProp || reactId
32
+ const [open, setOpen] = useState(false)
33
+ const isClient = useIsClient()
34
+
35
+ const handleToggle = (e: ToggleEvent) => {
36
+ setOpen(e.newState === 'open')
37
+ }
38
+
39
+ const triggerProps = {
40
+ popoverTarget: id,
41
+ style: {anchorName: `--anchor-${anchorName || id}`},
42
+ }
43
+
44
+ const trigger = children.type.forwardsTriggerProps
45
+ ? cloneElement(children, {triggerProps})
46
+ : cloneElement(children, mergeTriggerProps(children.props, forwardedTriggerProps, triggerProps))
47
+
48
+ return (
49
+ <>
50
+ {trigger}
51
+
52
+ {renderPortal(
53
+ <Activity mode={open ? 'visible' : 'hidden'}>
54
+ <div
55
+ className={clsx(
56
+ popoverClassName,
57
+ 'sui-px2 sui-py1 sui-radius2 sui-position-fixed sui-shadow2',
58
+ className,
59
+ )}
60
+ style={{
61
+ ...style,
62
+ positionAnchor: `--anchor-${anchorName || id}`,
63
+ }}
64
+ data-ui="Popover"
65
+ popover="auto"
66
+ id={id}
67
+ onToggle={handleToggle}
68
+ {...rest}
69
+ >
70
+ {content}
71
+ </div>
72
+ </Activity>,
73
+ isClient,
74
+ portal,
75
+ )}
76
+ </>
77
+ )
78
+ }
79
+
80
+ /** @beta */
81
+ export const Popover = Object.assign(PopoverRoot, {
82
+ forwardsTriggerProps: true,
83
+ })
84
+
85
+ export type {PopoverProps}
@@ -0,0 +1,21 @@
1
+ .sui-PopoverContent {
2
+ visibility: hidden;
3
+ opacity: 0;
4
+ scale: 0.9;
5
+ transition: none;
6
+ }
7
+
8
+ .sui-PopoverContent:popover-open {
9
+ visibility: visible;
10
+ opacity: 1;
11
+ scale: 1;
12
+ transition: var(--fade-in-transition);
13
+ }
14
+
15
+ @starting-style {
16
+ .sui-PopoverContent:popover-open {
17
+ visibility: hidden;
18
+ opacity: 0;
19
+ scale: 0.9;
20
+ }
21
+ }
@@ -0,0 +1,38 @@
1
+ import {type PlacementProps, placementProps} from '../../props/placement'
2
+ import {type PropDef} from '../../types/PropDef'
3
+
4
+ /** @beta */
5
+ export interface PopoverProps
6
+ extends Omit<React.ComponentProps<'div'>, 'children' | 'content'>, PlacementProps {
7
+ /**
8
+ * Shared anchor identifier.
9
+ * @remarks Set the same value on a Tooltip and a Popover to point both at one trigger.
10
+ */
11
+ anchorName?: React.ReactNode
12
+ /**
13
+ * The trigger element.
14
+ * @remarks Popover clones it and attaches the `popovertarget` attribute that opens the floating layer.
15
+ */
16
+ children: React.ReactElement<Record<string, unknown>>
17
+ /**
18
+ * The floating content.
19
+ */
20
+ content?: React.ReactNode
21
+ /**
22
+ * Renders the content into `document.body` through a React portal instead of inline.
23
+ */
24
+ portal?: boolean
25
+ }
26
+
27
+ export const popoverProps: Record<string, PropDef> = {
28
+ anchorName: {
29
+ type: 'string',
30
+ },
31
+ content: {
32
+ type: 'string',
33
+ },
34
+ portal: {
35
+ type: 'boolean',
36
+ },
37
+ ...placementProps,
38
+ }
@@ -3,7 +3,9 @@ import {type PropDef} from '../../types/PropDef'
3
3
 
4
4
  /** @public */
5
5
  export interface PressAreaProps<T extends React.ElementType = 'button'> {
6
- /** Element to render */
6
+ /**
7
+ * Element or component to render.
8
+ */
7
9
  as?: InteractiveAs<T>
8
10
  }
9
11
 
@@ -17,7 +17,7 @@ export function Radio(props: RadioProps) {
17
17
 
18
18
  return (
19
19
  <Label
20
- className={clsx(radioClassName, className)}
20
+ className={clsx(radioClassName, 'sui-position-relative', className)}
21
21
  style={style}
22
22
  data-ui="Radio"
23
23
  disabled={disabled}
@@ -3,9 +3,14 @@ import {type PropDef} from '../../types/PropDef'
3
3
 
4
4
  /** @beta */
5
5
  export interface RadioProps extends React.ComponentProps<'input'>, MarginProps {
6
- /** Error state */
6
+ /**
7
+ * Applies error styling to the radio mark.
8
+ * @remarks Use when form validation fails. Pair with a visible error message. The prop is visual only.
9
+ */
7
10
  error?: boolean
8
- /** Input label */
11
+ /**
12
+ * Visible label next to the radio.
13
+ */
9
14
  label: React.ReactNode
10
15
  }
11
16
 
@@ -0,0 +1,47 @@
1
+ import SelectIcon from '@sanity/icons/Select'
2
+ import clsx from 'clsx'
3
+ import {useId} from 'react'
4
+
5
+ import {getProps} from '../../utils/getProps'
6
+ import {suffixClassName} from '../../utils/suffixClassName'
7
+ import {Icon} from '../icon/Icon'
8
+ import {type SelectProps, selectProps} from './select.props'
9
+
10
+ const selectClassName = suffixClassName('sui-Select')
11
+
12
+ /** @public */
13
+ export function Select({density = 'regular', hasError = false, ...props}: SelectProps) {
14
+ const {children, className, style, ...rest} = getProps({density, ...props}, selectProps)
15
+
16
+ const anchorId = useId()
17
+ const anchorName = `--anchor-${anchorId}`
18
+
19
+ const selectClasses = clsx(
20
+ selectClassName,
21
+ hasError && 'sui-error',
22
+ 'sui-radius2',
23
+ 'sui-width-full',
24
+ className,
25
+ )
26
+
27
+ const iconClasses = `sui-position-absolute sui-display-flex sui-align-items-center sui-px1`
28
+
29
+ return (
30
+ <>
31
+ <select
32
+ data-ui="Select"
33
+ className={selectClasses}
34
+ style={{...style, anchorName}}
35
+ {...rest}
36
+ aria-invalid={hasError || undefined}
37
+ >
38
+ {children}
39
+ </select>
40
+ <div className={iconClasses} data-ui="Select-icon" style={{positionAnchor: anchorName}}>
41
+ <Icon icon={SelectIcon} size={1} />
42
+ </div>
43
+ </>
44
+ )
45
+ }
46
+
47
+ export type {SelectProps}
@@ -0,0 +1,67 @@
1
+ .sui-Select {
2
+ --background-color: var(--backdrop);
3
+ --border-color: var(--separator-high);
4
+ --color: var(--foreground-high);
5
+ background-color: var(--background-color);
6
+ border: 1px solid var(--border-color);
7
+ font: var(--form-element-text);
8
+ color: var(--color);
9
+ }
10
+
11
+ [data-ui="Select-icon"] {
12
+ --background-color: var(--backdrop);
13
+ background-color: var(--background-color);
14
+ pointer-events: none;
15
+ /* reduce block size by 1px at each end and inset from right to not interrupt Select’s border */
16
+ top: calc(anchor(top) + 1px);
17
+ bottom: calc(anchor(bottom) + 1px);
18
+ right: calc(anchor(right) + 2px);
19
+ }
20
+
21
+ /**
22
+ * Error
23
+ */
24
+ .sui-Select.sui-error {
25
+ --border-color: var(--error-separator);
26
+ --color: var(--error-foreground);
27
+ }
28
+
29
+ .sui-Select.sui-error + [data-ui="Select-icon"] svg {
30
+ color: var(--error-foreground);
31
+ }
32
+
33
+ /**
34
+ * Hover - uses relative border/background colours to darken/lighten in both normal & error state
35
+ */
36
+ .sui-Select:hover {
37
+ border-color: light-dark(
38
+ oklch(from var(--border-color) calc(l - var(--hover-lightness-delta)) c h),
39
+ oklch(from var(--border-color) calc(l + var(--hover-lightness-delta)) c h)
40
+ );
41
+ }
42
+
43
+ /**
44
+ * Disabled
45
+ */
46
+ .sui-Select:disabled {
47
+ --background-color: var(--inert-background);
48
+ --border-color: var(--separator-low);
49
+ --color: var(--inert-foreground);
50
+ pointer-events: none;
51
+ }
52
+
53
+ .sui-Select:disabled + [data-ui="Select-icon"] {
54
+ --background-color: var(--inert-background);
55
+ }
56
+
57
+ .sui-Select:disabled + [data-ui="Select-icon"] svg {
58
+ color: var(--inert-foreground);
59
+ }
60
+
61
+ /**
62
+ * Focus
63
+ */
64
+ .sui-Select:focus-visible {
65
+ outline: var(--focus-outline);
66
+ outline-offset: var(--focus-offset);
67
+ }
@@ -0,0 +1,25 @@
1
+ import {formElementDensityProps, type FormElementDensityProps} from '../../props/formElement'
2
+ import {type PropDef} from '../../types/PropDef'
3
+
4
+ /** @public */
5
+ export type SelectProps = React.ComponentProps<'select'> &
6
+ FormElementDensityProps & {
7
+ /**
8
+ * Disables the select element, blocking interactions and hiding it from screen readers.
9
+ */
10
+ disabled?: boolean
11
+ /**
12
+ * Used to trigger styling for invalid states.
13
+ */
14
+ hasError?: boolean
15
+ }
16
+
17
+ export const selectProps: Record<string, PropDef> = {
18
+ disabled: {
19
+ type: 'boolean',
20
+ },
21
+ hasError: {
22
+ type: 'boolean',
23
+ },
24
+ ...formElementDensityProps,
25
+ }
@@ -3,12 +3,18 @@ import {type PropDef} from '../../types/PropDef'
3
3
  /** @beta */
4
4
  export interface SkipToContentProps extends React.ComponentProps<'a'> {
5
5
  /**
6
- * Anchor href. The target element should be focusable or have `tabindex="-1"`.
6
+ * Same-page fragment to jump to.
7
+ * @remarks Note: The hash character # is required for proper linking.
7
8
  */
8
9
  href: string
9
- /** Visible/announced label */
10
+ /**
11
+ * Visible and announced text.
12
+ */
10
13
  label: string
11
- /** Use label instead */
14
+ /**
15
+ * Not accepted.
16
+ * @remarks Pass text via `label`.
17
+ */
12
18
  children?: never
13
19
  }
14
20