@pantoia/ui 0.2.0 → 0.4.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 (66) hide show
  1. package/accordion/index.module.css +4 -4
  2. package/accordion/index.tsx +30 -58
  3. package/alert-dialog/index.tsx +38 -44
  4. package/badge/index.tsx +2 -2
  5. package/button/index.tsx +31 -30
  6. package/callout/index.tsx +29 -8
  7. package/checkbox/index.tsx +24 -88
  8. package/combobox/index.tsx +133 -323
  9. package/command-palette/index.tsx +55 -34
  10. package/dialog/index.tsx +30 -26
  11. package/dist/index.js +155 -27
  12. package/drawer/index.module.css +2 -2
  13. package/drawer/index.tsx +54 -132
  14. package/dropdown-menu/index.tsx +95 -100
  15. package/field/index.tsx +43 -94
  16. package/input/index.tsx +13 -9
  17. package/kbd/index.tsx +2 -2
  18. package/label/index.tsx +7 -9
  19. package/number-field/index.tsx +129 -176
  20. package/package.json +12 -15
  21. package/palette.css +114 -0
  22. package/popover/index.tsx +62 -33
  23. package/radio-group/index.tsx +36 -39
  24. package/registry.json +65 -169
  25. package/select/index.tsx +154 -76
  26. package/switch/index.tsx +13 -6
  27. package/table/index.tsx +5 -6
  28. package/tabs/index.module.css +5 -3
  29. package/tabs/index.tsx +26 -77
  30. package/tailwind.css +3 -0
  31. package/textarea/index.tsx +25 -13
  32. package/toast/index.module.css +187 -0
  33. package/toast/index.tsx +141 -46
  34. package/toggle/index.tsx +8 -7
  35. package/{styles.css → tokens.css} +82 -303
  36. package/tooltip/index.tsx +62 -31
  37. package/tv.ts +0 -1
  38. package/utilities.css +124 -0
  39. package/accordion/AGENTS.md +0 -60
  40. package/alert-dialog/AGENTS.md +0 -78
  41. package/badge/AGENTS.md +0 -35
  42. package/button/AGENTS.md +0 -39
  43. package/callout/AGENTS.md +0 -41
  44. package/checkbox/AGENTS.md +0 -54
  45. package/combobox/AGENTS.md +0 -150
  46. package/command-palette/AGENTS.md +0 -45
  47. package/dialog/AGENTS.md +0 -81
  48. package/drawer/AGENTS.md +0 -66
  49. package/dropdown-menu/AGENTS.md +0 -133
  50. package/field/AGENTS.md +0 -96
  51. package/input/AGENTS.md +0 -35
  52. package/kbd/AGENTS.md +0 -17
  53. package/label/AGENTS.md +0 -21
  54. package/number-field/AGENTS.md +0 -53
  55. package/popover/AGENTS.md +0 -64
  56. package/press.css +0 -37
  57. package/radio-group/AGENTS.md +0 -81
  58. package/select/AGENTS.md +0 -90
  59. package/switch/AGENTS.md +0 -37
  60. package/table/AGENTS.md +0 -82
  61. package/tabs/AGENTS.md +0 -60
  62. package/test-utils.ts +0 -10
  63. package/textarea/AGENTS.md +0 -35
  64. package/toast/AGENTS.md +0 -34
  65. package/toggle/AGENTS.md +0 -37
  66. package/tooltip/AGENTS.md +0 -50
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pantoia/ui",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Pantoia Design System",
5
5
  "homepage": "https://design.pantoia.com",
6
6
  "repository": {
@@ -16,20 +16,21 @@
16
16
  "files": [
17
17
  "*/index.tsx",
18
18
  "*/index.module.css",
19
- "*/AGENTS.md",
20
19
  "dist",
21
20
  "tv.ts",
22
- "test-utils.ts",
23
- "styles.css",
24
- "press.css",
21
+ "tailwind.css",
22
+ "palette.css",
23
+ "tokens.css",
24
+ "utilities.css",
25
25
  "registry.json"
26
26
  ],
27
27
  "exports": {
28
28
  "./tv": "./tv.ts",
29
- "./styles.css": "./styles.css",
30
- "./press.css": "./press.css",
29
+ "./tailwind.css": "./tailwind.css",
30
+ "./palette.css": "./palette.css",
31
+ "./tokens.css": "./tokens.css",
32
+ "./utilities.css": "./utilities.css",
31
33
  "./registry.json": "./registry.json",
32
- "./test-utils": "./test-utils.ts",
33
34
  "./*": "./*/index.tsx"
34
35
  },
35
36
  "sideEffects": [
@@ -39,24 +40,20 @@
39
40
  "access": "public"
40
41
  },
41
42
  "engines": {
42
- "node": ">=24"
43
+ "node": ">=20.11"
43
44
  },
44
45
  "scripts": {
45
46
  "prepack": "tsc cli/index.ts --ignoreConfig --outDir dist --target es2022 --module esnext --moduleResolution bundler --skipLibCheck --types node",
46
47
  "typecheck": "tsc --noEmit"
47
48
  },
48
49
  "dependencies": {
49
- "@fontsource/jetbrains-mono": "5.3.0",
50
- "lucide-react": "^1.39.0",
51
- "pretendard": "1.3.9",
52
- "radix-ui": "^1.6.7",
53
- "sonner": "^2.0.8",
50
+ "@base-ui/react": "^1.8.0",
54
51
  "tailwind-variants": "^3.3.1"
55
52
  },
56
53
  "peerDependencies": {
57
54
  "react": "^19",
58
55
  "react-dom": "^19",
59
- "tailwindcss": "^4.3.3"
56
+ "tailwindcss": "^4.0.0"
60
57
  },
61
58
  "devDependencies": {
62
59
  "@pantoia/typescript-config": "*",
package/palette.css ADDED
@@ -0,0 +1,114 @@
1
+ :root, .light {
2
+ --pds-gray-50:#f9fafb; --pds-gray-100:#f2f4f6; --pds-gray-200:#e5e8eb; --pds-gray-300:#d1d6db;
3
+ --pds-gray-400:#b0b8c1; --pds-gray-500:#8b95a1; --pds-gray-600:#6b7684; --pds-gray-700:#4e5968;
4
+ --pds-gray-800:#333d4b; --pds-gray-900:#191f28;
5
+
6
+ --pds-brand-50:#f5f1fd; --pds-brand-100:#e4d6ff; --pds-brand-200:#ceb0ff; --pds-brand-300:#bb8ffa;
7
+ --pds-brand-400:#aa74f2; --pds-brand-500:#9c63e4; --pds-brand-600:#8750c9; --pds-brand-700:#7543b0;
8
+ --pds-brand-800:#633997; --pds-brand-900:#54317f;
9
+
10
+ --pds-blue-50:#e8f3ff; --pds-blue-100:#c9e2ff; --pds-blue-200:#90c2ff; --pds-blue-300:#64a8ff;
11
+ --pds-blue-400:#4593fc; --pds-blue-500:#3182f6; --pds-blue-600:#2272eb; --pds-blue-700:#1b64da;
12
+ --pds-blue-800:#1957c2; --pds-blue-900:#194aa6;
13
+
14
+ --pds-green-50:#f0faf6; --pds-green-100:#aeefd5; --pds-green-200:#76e4b8; --pds-green-300:#3fd599;
15
+ --pds-green-400:#15c47e; --pds-green-500:#03b26c; --pds-green-600:#02a262; --pds-green-700:#029359;
16
+ --pds-green-800:#028450; --pds-green-900:#027648;
17
+
18
+ --pds-orange-50:#fff3e0; --pds-orange-100:#ffe0b0; --pds-orange-200:#ffcd80; --pds-orange-300:#ffbd51;
19
+ --pds-orange-400:#ffa927; --pds-orange-500:#fe9800; --pds-orange-600:#fb8800; --pds-orange-700:#f57800;
20
+ --pds-orange-800:#ed6700; --pds-orange-900:#e45600;
21
+
22
+ --pds-red-50:#ffeeee; --pds-red-100:#ffd4d6; --pds-red-200:#feafb4; --pds-red-300:#fb8890;
23
+ --pds-red-400:#f66570; --pds-red-500:#f04452; --pds-red-600:#e42939; --pds-red-700:#d22030;
24
+ --pds-red-800:#bc1b2a; --pds-red-900:#a51926;
25
+ }
26
+
27
+ .dark {
28
+ --pds-gray-50:#202027; --pds-gray-100:#2c2c35; --pds-gray-200:#3c3c47; --pds-gray-300:#4d4d59;
29
+ --pds-gray-400:#62626d; --pds-gray-500:#7e7e87; --pds-gray-600:#9e9ea4; --pds-gray-700:#c3c3c6;
30
+ --pds-gray-800:#e4e4e5; --pds-gray-900:#ffffff;
31
+
32
+ --pds-brand-50:#362949; --pds-brand-100:#462e66; --pds-brand-200:#583682; --pds-brand-300:#6b3fa0;
33
+ --pds-brand-400:#8451c4; --pds-brand-500:#9c63e4; --pds-brand-600:#ad75f7; --pds-brand-700:#c094ff;
34
+ --pds-brand-800:#d6baff; --pds-brand-900:#eadeff;
35
+
36
+ --pds-blue-50:#202c4d; --pds-blue-100:#23386a; --pds-blue-200:#25478c; --pds-blue-300:#265ab3;
37
+ --pds-blue-400:#2970d9; --pds-blue-500:#3485fa; --pds-blue-600:#449bff; --pds-blue-700:#61b0ff;
38
+ --pds-blue-800:#8fcdff; --pds-blue-900:#c8e7ff;
39
+
40
+ --pds-green-50:#153729; --pds-green-100:#135338; --pds-green-200:#136d47; --pds-green-300:#138a59;
41
+ --pds-green-400:#13a065; --pds-green-500:#16bb76; --pds-green-600:#26cf88; --pds-green-700:#4ee4a6;
42
+ --pds-green-800:#82f6c5; --pds-green-900:#ccffea;
43
+
44
+ --pds-orange-50:#3d2500; --pds-orange-100:#563200; --pds-orange-200:#804600; --pds-orange-300:#a85f00;
45
+ --pds-orange-400:#cf7200; --pds-orange-500:#f18600; --pds-orange-600:#fd9528; --pds-orange-700:#ffa861;
46
+ --pds-orange-800:#ffc39e; --pds-orange-900:#ffe4d6;
47
+
48
+ --pds-red-50:#3c2020; --pds-red-100:#562025; --pds-red-200:#7a242d; --pds-red-300:#9e2733;
49
+ --pds-red-400:#ca2f3d; --pds-red-500:#f04251; --pds-red-600:#fa616d; --pds-red-700:#fe818b;
50
+ --pds-red-800:#ffa8ad; --pds-red-900:#ffd1d3;
51
+ }
52
+
53
+ @theme inline {
54
+ --color-pds-gray-50: var(--pds-gray-50);
55
+ --color-pds-gray-100: var(--pds-gray-100);
56
+ --color-pds-gray-200: var(--pds-gray-200);
57
+ --color-pds-gray-300: var(--pds-gray-300);
58
+ --color-pds-gray-400: var(--pds-gray-400);
59
+ --color-pds-gray-500: var(--pds-gray-500);
60
+ --color-pds-gray-600: var(--pds-gray-600);
61
+ --color-pds-gray-700: var(--pds-gray-700);
62
+ --color-pds-gray-800: var(--pds-gray-800);
63
+ --color-pds-gray-900: var(--pds-gray-900);
64
+ --color-pds-brand-50: var(--pds-brand-50);
65
+ --color-pds-brand-100: var(--pds-brand-100);
66
+ --color-pds-brand-200: var(--pds-brand-200);
67
+ --color-pds-brand-300: var(--pds-brand-300);
68
+ --color-pds-brand-400: var(--pds-brand-400);
69
+ --color-pds-brand-500: var(--pds-brand-500);
70
+ --color-pds-brand-600: var(--pds-brand-600);
71
+ --color-pds-brand-700: var(--pds-brand-700);
72
+ --color-pds-brand-800: var(--pds-brand-800);
73
+ --color-pds-brand-900: var(--pds-brand-900);
74
+ --color-pds-blue-50: var(--pds-blue-50);
75
+ --color-pds-blue-100: var(--pds-blue-100);
76
+ --color-pds-blue-200: var(--pds-blue-200);
77
+ --color-pds-blue-300: var(--pds-blue-300);
78
+ --color-pds-blue-400: var(--pds-blue-400);
79
+ --color-pds-blue-500: var(--pds-blue-500);
80
+ --color-pds-blue-600: var(--pds-blue-600);
81
+ --color-pds-blue-700: var(--pds-blue-700);
82
+ --color-pds-blue-800: var(--pds-blue-800);
83
+ --color-pds-blue-900: var(--pds-blue-900);
84
+ --color-pds-green-50: var(--pds-green-50);
85
+ --color-pds-green-100: var(--pds-green-100);
86
+ --color-pds-green-200: var(--pds-green-200);
87
+ --color-pds-green-300: var(--pds-green-300);
88
+ --color-pds-green-400: var(--pds-green-400);
89
+ --color-pds-green-500: var(--pds-green-500);
90
+ --color-pds-green-600: var(--pds-green-600);
91
+ --color-pds-green-700: var(--pds-green-700);
92
+ --color-pds-green-800: var(--pds-green-800);
93
+ --color-pds-green-900: var(--pds-green-900);
94
+ --color-pds-orange-50: var(--pds-orange-50);
95
+ --color-pds-orange-100: var(--pds-orange-100);
96
+ --color-pds-orange-200: var(--pds-orange-200);
97
+ --color-pds-orange-300: var(--pds-orange-300);
98
+ --color-pds-orange-400: var(--pds-orange-400);
99
+ --color-pds-orange-500: var(--pds-orange-500);
100
+ --color-pds-orange-600: var(--pds-orange-600);
101
+ --color-pds-orange-700: var(--pds-orange-700);
102
+ --color-pds-orange-800: var(--pds-orange-800);
103
+ --color-pds-orange-900: var(--pds-orange-900);
104
+ --color-pds-red-50: var(--pds-red-50);
105
+ --color-pds-red-100: var(--pds-red-100);
106
+ --color-pds-red-200: var(--pds-red-200);
107
+ --color-pds-red-300: var(--pds-red-300);
108
+ --color-pds-red-400: var(--pds-red-400);
109
+ --color-pds-red-500: var(--pds-red-500);
110
+ --color-pds-red-600: var(--pds-red-600);
111
+ --color-pds-red-700: var(--pds-red-700);
112
+ --color-pds-red-800: var(--pds-red-800);
113
+ --color-pds-red-900: var(--pds-red-900);
114
+ }
package/popover/index.tsx CHANGED
@@ -1,49 +1,74 @@
1
1
  'use client'
2
2
 
3
- import { Popover as PopoverPrimitive } from 'radix-ui'
4
- import type { ComponentProps, ReactNode } from 'react'
3
+ import { Popover as PopoverPrimitive } from '@base-ui/react/popover'
4
+ import { useRender } from '@base-ui/react/use-render'
5
+ import { createContext, useContext, useState } from 'react'
6
+ import type { ComponentProps, Dispatch, SetStateAction } from 'react'
5
7
 
6
8
  import { tv } from '../tv'
7
9
 
8
- export interface PopoverProps {
9
- open?: boolean
10
- defaultOpen?: boolean
11
- onOpenChange?: (open: boolean) => void
12
- children?: ReactNode
13
- }
10
+ type AnchorState = [Element | null, Dispatch<SetStateAction<Element | null>>]
11
+
12
+ const AnchorContext = createContext<AnchorState>([null, () => {}])
13
+
14
+ export type PopoverProps = ComponentProps<typeof PopoverPrimitive.Root>
15
+
16
+ export function Popover(props: PopoverProps) {
17
+ const anchor = useState<Element | null>(null)
14
18
 
15
- export function Popover({ open, defaultOpen, onOpenChange, children }: PopoverProps) {
16
19
  return (
17
- <PopoverPrimitive.Root open={open} defaultOpen={defaultOpen} onOpenChange={onOpenChange}>
18
- {children}
19
- </PopoverPrimitive.Root>
20
+ <AnchorContext.Provider value={anchor}>
21
+ <PopoverPrimitive.Root {...props} />
22
+ </AnchorContext.Provider>
20
23
  )
21
24
  }
22
25
 
23
- export function PopoverTrigger({ children }: { children: ReactNode }) {
24
- return <PopoverPrimitive.Trigger asChild>{children}</PopoverPrimitive.Trigger>
26
+ export type PopoverTriggerProps = ComponentProps<typeof PopoverPrimitive.Trigger>
27
+
28
+ export function PopoverTrigger(props: PopoverTriggerProps) {
29
+ return <PopoverPrimitive.Trigger {...props} />
25
30
  }
26
31
 
27
- export function PopoverAnchor({ children }: { children: ReactNode }) {
28
- return <PopoverPrimitive.Anchor asChild>{children}</PopoverPrimitive.Anchor>
32
+ export type PopoverAnchorProps = useRender.ComponentProps<'div'>
33
+
34
+ export function PopoverAnchor({ render, ref, ...rest }: PopoverAnchorProps) {
35
+ const [, setAnchor] = useContext(AnchorContext)
36
+
37
+ return useRender({
38
+ defaultTagName: 'div',
39
+ render,
40
+ ref: ref ? [setAnchor, ref] : setAnchor,
41
+ props: rest,
42
+ })
29
43
  }
30
44
 
31
- export function PopoverClose({ children }: { children: ReactNode }) {
32
- return <PopoverPrimitive.Close asChild>{children}</PopoverPrimitive.Close>
45
+ export type PopoverCloseProps = ComponentProps<typeof PopoverPrimitive.Close>
46
+
47
+ export function PopoverClose(props: PopoverCloseProps) {
48
+ return <PopoverPrimitive.Close {...props} />
33
49
  }
34
50
 
35
- export interface PopoverContentProps extends Omit<ComponentProps<typeof PopoverPrimitive.Content>, 'side' | 'align'> {
51
+ export type PopoverContentProps = Omit<ComponentProps<typeof PopoverPrimitive.Popup>, 'className'> & {
36
52
  side?: 'top' | 'right' | 'bottom' | 'left'
37
53
  align?: 'start' | 'center' | 'end'
38
54
  arrow?: boolean
55
+ className?: string
39
56
  }
40
57
 
41
58
  const popoverContent = tv({
42
- base: 'float text-foreground rounded-float z-overlay p-4'
59
+ base: 'float text-fg rounded-r16 z-overlay p-4'
43
60
  + ' open:animate-enter closed:animate-exit'
44
61
  + ' focus-visible:outline-none',
45
62
  })
46
63
 
64
+ const popoverArrow = tv({
65
+ base: 'data-uncentered:invisible'
66
+ + ' side-top:top-full'
67
+ + ' side-bottom:bottom-full side-bottom:rotate-180'
68
+ + ' side-right:left-0 side-right:origin-top-left side-right:rotate-90 side-right:-translate-y-1/2'
69
+ + ' side-left:right-0 side-left:origin-top-right side-left:-rotate-90 side-left:-translate-y-1/2',
70
+ })
71
+
47
72
  export function PopoverContent({
48
73
  side = 'bottom',
49
74
  align = 'center',
@@ -52,25 +77,29 @@ export function PopoverContent({
52
77
  children,
53
78
  ...rest
54
79
  }: PopoverContentProps) {
80
+ const [anchor] = useContext(AnchorContext)
81
+
55
82
  return (
56
83
  <PopoverPrimitive.Portal>
57
- <PopoverPrimitive.Content
58
- {...rest}
84
+ <PopoverPrimitive.Positioner
85
+ anchor={anchor ?? undefined}
59
86
  side={side}
60
87
  align={align}
61
- sideOffset={8}
88
+ sideOffset={arrow ? 14 : 8}
62
89
  arrowPadding={16}
63
- className={popoverContent({ className })}
90
+ className="z-overlay min-w-max will-change-transform"
64
91
  >
65
- {children}
66
- {arrow && (
67
- <PopoverPrimitive.Arrow asChild width={12} height={6}>
68
- <svg viewBox="0 0 12 6" preserveAspectRatio="none" className="fill-background-panel -translate-y-px">
69
- <path d="M0 0L4.58579 4.58579C5.36683 5.36683 6.63316 5.36684 7.41421 4.58579L12 0" className="stroke-line stroke-1" />
70
- </svg>
71
- </PopoverPrimitive.Arrow>
72
- )}
73
- </PopoverPrimitive.Content>
92
+ <PopoverPrimitive.Popup {...rest} className={popoverContent({ className })}>
93
+ {children}
94
+ {arrow && (
95
+ <PopoverPrimitive.Arrow className={popoverArrow()}>
96
+ <svg width={12} height={6} viewBox="0 0 12 6" preserveAspectRatio="none" className="block fill-bg-panel -translate-y-px">
97
+ <path d="M0 0L4.58579 4.58579C5.36683 5.36683 6.63316 5.36684 7.41421 4.58579L12 0" className="stroke-line stroke-1" />
98
+ </svg>
99
+ </PopoverPrimitive.Arrow>
100
+ )}
101
+ </PopoverPrimitive.Popup>
102
+ </PopoverPrimitive.Positioner>
74
103
  </PopoverPrimitive.Portal>
75
104
  )
76
105
  }
@@ -1,7 +1,8 @@
1
1
  'use client'
2
2
 
3
- import { RadioGroup as RadioGroupPrimitive } from 'radix-ui'
4
- import { createContext, use, useId, type ReactNode } from 'react'
3
+ import { Radio } from '@base-ui/react/radio'
4
+ import { RadioGroup as RadioGroupPrimitive } from '@base-ui/react/radio-group'
5
+ import { cloneElement, createContext, use, useEffect, useId, useState, type ComponentProps, type ReactElement, type ReactNode } from 'react'
5
6
 
6
7
  import { Label } from '../label'
7
8
  import { tv } from '../tv'
@@ -14,6 +15,7 @@ export interface RadioItemContext {
14
15
  id: string
15
16
  labelId: string
16
17
  descriptionId: string
18
+ setDescribed: (described: boolean) => void
17
19
  }
18
20
 
19
21
  const ItemContext = createContext<RadioItemContext | null>(null)
@@ -44,69 +46,57 @@ export const radioGroupItem = tv({
44
46
  defaultVariants: { color: 'neutral' },
45
47
  })
46
48
 
47
- export interface RadioGroupProps {
48
- value?: string
49
- defaultValue?: string
50
- onValueChange?: (value: string) => void
49
+ export type RadioGroupProps = Omit<RadioGroupPrimitive.Props<string>, 'className'> & {
51
50
  color?: RadioColor
52
- disabled?: boolean
53
51
  className?: string
54
- children?: ReactNode
55
52
  }
56
53
 
57
54
  const radioGroup = tv({ base: 'flex flex-col gap-3' })
58
55
 
59
- export function RadioGroup({
60
- value,
61
- defaultValue,
62
- onValueChange,
63
- color = 'neutral',
64
- disabled = false,
65
- className,
66
- children,
67
- ...rest
68
- }: RadioGroupProps) {
56
+ export function RadioGroup({ color = 'neutral', className, ...rest }: RadioGroupProps) {
69
57
  return (
70
58
  <ColorContext value={color}>
71
- <RadioGroupPrimitive.Root
72
- {...rest}
73
- value={value}
74
- defaultValue={defaultValue}
75
- onValueChange={onValueChange}
76
- disabled={disabled}
77
- className={radioGroup({ className })}
78
- >
79
- {children}
80
- </RadioGroupPrimitive.Root>
59
+ <RadioGroupPrimitive<string> {...rest} className={radioGroup({ className })} />
81
60
  </ColorContext>
82
61
  )
83
62
  }
84
63
 
85
- export interface RadioGroupItemProps {
64
+ export type RadioGroupItemProps = Omit<ComponentProps<typeof Radio.Root>, 'className' | 'value'> & {
86
65
  value: string
87
- disabled?: boolean
88
66
  className?: string
89
- children?: ReactNode
90
67
  }
91
68
 
92
- export function RadioGroupItem({ value, disabled, className, children }: RadioGroupItemProps) {
69
+ export function RadioGroupItem({
70
+ value,
71
+ className,
72
+ children,
73
+ nativeButton = true,
74
+ render = <button type="button" />,
75
+ ...rest
76
+ }: RadioGroupItemProps) {
93
77
  const id = useId()
78
+ const [described, setDescribed] = useState(false)
94
79
  const color = use(ColorContext)
95
80
  const ui = radioGroupItem({ color })
81
+ const renderWithId: RadioGroupItemProps['render'] = typeof render === 'function'
82
+ ? (props, state) => render({ ...props, id }, state)
83
+ : cloneElement(render as ReactElement<{ id?: string }>, { id })
96
84
 
97
85
  return (
98
- <ItemContext value={{ id, labelId: `${id}-label`, descriptionId: `${id}-description` }}>
86
+ <ItemContext value={{ id, labelId: `${id}-label`, descriptionId: `${id}-description`, setDescribed }}>
99
87
  <div className={ui.root({ className })}>
100
- <RadioGroupPrimitive.Item
88
+ <Radio.Root<string>
89
+ {...rest}
90
+ nativeButton={nativeButton}
91
+ render={renderWithId}
101
92
  id={id}
102
93
  value={value}
103
- disabled={disabled}
104
94
  aria-labelledby={`${id}-label`}
105
- aria-describedby={`${id}-description`}
95
+ aria-describedby={described ? `${id}-description` : undefined}
106
96
  className={ui.control()}
107
97
  >
108
- <RadioGroupPrimitive.Indicator className="text-fg-neutral-solid-contrast size-2.5 rounded-full bg-current" />
109
- </RadioGroupPrimitive.Item>
98
+ <Radio.Indicator className="text-fg-neutral-solid-contrast size-2.5 rounded-full bg-current" />
99
+ </Radio.Root>
110
100
 
111
101
  <div className="flex min-w-0 flex-col gap-0.5">
112
102
  {children}
@@ -126,10 +116,17 @@ export function RadioGroupItemLabel({ className, children }: { className?: strin
126
116
  )
127
117
  }
128
118
 
129
- const radioGroupItemDescription = tv({ base: 'text-caption text-foreground-subtle' })
119
+ const radioGroupItemDescription = tv({ base: 'text-caption text-fg-subtle' })
130
120
 
131
121
  export function RadioGroupItemDescription({ className, children }: { className?: string, children?: ReactNode }) {
132
122
  const ctx = use(ItemContext)
123
+ const setDescribed = ctx?.setDescribed
124
+
125
+ useEffect(() => {
126
+ if (!setDescribed) return
127
+ setDescribed(true)
128
+ return () => setDescribed(false)
129
+ }, [setDescribed])
133
130
 
134
131
  return (
135
132
  <p