@g4rcez/components 3.0.0 → 3.0.1

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 (176) hide show
  1. package/dist/ai/SKILL.md +266 -0
  2. package/dist/ai/docs/Alert.md +167 -0
  3. package/dist/ai/docs/AnimatedList.md +205 -0
  4. package/dist/ai/docs/Autocomplete.md +225 -0
  5. package/dist/ai/docs/Button.md +182 -0
  6. package/dist/ai/docs/Calendar.md +219 -0
  7. package/dist/ai/docs/Card.md +174 -0
  8. package/dist/ai/docs/Checkbox.md +199 -0
  9. package/dist/ai/docs/CommandPalette.md +293 -0
  10. package/dist/ai/docs/DatePicker.md +171 -0
  11. package/dist/ai/docs/Dropdown.md +223 -0
  12. package/dist/ai/docs/Empty.md +163 -0
  13. package/dist/ai/docs/Expand.md +143 -0
  14. package/dist/ai/docs/FileUpload.md +225 -0
  15. package/dist/ai/docs/Form.md +107 -0
  16. package/dist/ai/docs/FormReset.md +117 -0
  17. package/dist/ai/docs/Heading.md +88 -0
  18. package/dist/ai/docs/Input.md +237 -0
  19. package/dist/ai/docs/InputField.md +170 -0
  20. package/dist/ai/docs/List.md +205 -0
  21. package/dist/ai/docs/Menu.md +166 -0
  22. package/dist/ai/docs/Modal.md +280 -0
  23. package/dist/ai/docs/MultiSelect.md +196 -0
  24. package/dist/ai/docs/Notifications.md +231 -0
  25. package/dist/ai/docs/PageCalendar.md +271 -0
  26. package/dist/ai/docs/Polymorph.md +159 -0
  27. package/dist/ai/docs/Progress.md +145 -0
  28. package/dist/ai/docs/Radiobox.md +128 -0
  29. package/dist/ai/docs/RenderOnView.md +138 -0
  30. package/dist/ai/docs/Resizable.md +159 -0
  31. package/dist/ai/docs/Select.md +284 -0
  32. package/dist/ai/docs/Shortcut.md +105 -0
  33. package/dist/ai/docs/Skeleton.md +166 -0
  34. package/dist/ai/docs/Slider.md +144 -0
  35. package/dist/ai/docs/Slot.md +173 -0
  36. package/dist/ai/docs/Spinner.md +118 -0
  37. package/dist/ai/docs/Stats.md +137 -0
  38. package/dist/ai/docs/Step.md +159 -0
  39. package/dist/ai/docs/Switch.md +167 -0
  40. package/dist/ai/docs/Table.md +298 -0
  41. package/dist/ai/docs/Tabs.md +191 -0
  42. package/dist/ai/docs/Tag.md +224 -0
  43. package/dist/ai/docs/TaskList.md +144 -0
  44. package/dist/ai/docs/Textarea.md +167 -0
  45. package/dist/ai/docs/Timeline.md +210 -0
  46. package/dist/ai/docs/Toolbar.md +132 -0
  47. package/dist/ai/docs/Tooltip.md +231 -0
  48. package/dist/ai/docs/TransferList.md +142 -0
  49. package/dist/ai/docs/Typography.md +187 -0
  50. package/dist/ai/docs/Wizard.md +213 -0
  51. package/dist/ai/docs/index.md +183 -0
  52. package/dist/components/core/tag.d.ts +1 -1
  53. package/dist/components/core/tag.d.ts.map +1 -1
  54. package/dist/components/display/list.d.ts.map +1 -1
  55. package/dist/components/floating/dropdown.d.ts +1 -0
  56. package/dist/components/floating/dropdown.d.ts.map +1 -1
  57. package/dist/components/floating/menu.d.ts.map +1 -1
  58. package/dist/config/default-translations.d.ts +4 -4
  59. package/dist/hooks/use-translations.d.ts +4 -4
  60. package/dist/hooks/use-translations.d.ts.map +1 -1
  61. package/dist/index.css +1 -1
  62. package/dist/index.js +28 -20
  63. package/dist/index.js.map +1 -1
  64. package/dist/index.mjs +2463 -2458
  65. package/dist/index.mjs.map +1 -1
  66. package/dist/index.umd.js +12 -12
  67. package/dist/index.umd.js.map +1 -1
  68. package/package.json +4 -4
  69. package/dist/components/core/button.jsx +0 -79
  70. package/dist/components/core/heading.jsx +0 -4
  71. package/dist/components/core/polymorph.jsx +0 -5
  72. package/dist/components/core/render-on-view.jsx +0 -31
  73. package/dist/components/core/resizable.jsx +0 -51
  74. package/dist/components/core/slot.jsx +0 -156
  75. package/dist/components/core/tag.jsx +0 -51
  76. package/dist/components/core/typography.jsx +0 -22
  77. package/dist/components/display/alert.jsx +0 -58
  78. package/dist/components/display/calendar.jsx +0 -299
  79. package/dist/components/display/card.jsx +0 -43
  80. package/dist/components/display/empty.jsx +0 -11
  81. package/dist/components/display/list.jsx +0 -81
  82. package/dist/components/display/notifications.jsx +0 -126
  83. package/dist/components/display/progress.jsx +0 -11
  84. package/dist/components/display/shortcut.jsx +0 -23
  85. package/dist/components/display/skeleton.jsx +0 -12
  86. package/dist/components/display/spinner.jsx +0 -7
  87. package/dist/components/display/stats.jsx +0 -20
  88. package/dist/components/display/step.jsx +0 -131
  89. package/dist/components/display/tabs.jsx +0 -98
  90. package/dist/components/display/timeline.jsx +0 -25
  91. package/dist/components/floating/command-palette.jsx +0 -194
  92. package/dist/components/floating/dropdown.jsx +0 -53
  93. package/dist/components/floating/expand.jsx +0 -44
  94. package/dist/components/floating/menu.jsx +0 -147
  95. package/dist/components/floating/modal.jsx +0 -299
  96. package/dist/components/floating/toolbar.jsx +0 -5
  97. package/dist/components/floating/tooltip.jsx +0 -58
  98. package/dist/components/floating/wizard.jsx +0 -161
  99. package/dist/components/form/autocomplete.jsx +0 -279
  100. package/dist/components/form/checkbox.jsx +0 -12
  101. package/dist/components/form/date-picker.jsx +0 -115
  102. package/dist/components/form/file-upload.jsx +0 -133
  103. package/dist/components/form/form.jsx +0 -10
  104. package/dist/components/form/formReset.jsx +0 -17
  105. package/dist/components/form/free-text.jsx +0 -41
  106. package/dist/components/form/input-field.jsx +0 -56
  107. package/dist/components/form/input.jsx +0 -36
  108. package/dist/components/form/multi-select.jsx +0 -328
  109. package/dist/components/form/radiobox.jsx +0 -6
  110. package/dist/components/form/select.jsx +0 -42
  111. package/dist/components/form/slider.jsx +0 -45
  112. package/dist/components/form/switch.jsx +0 -46
  113. package/dist/components/form/task-list.jsx +0 -26
  114. package/dist/components/form/textarea.jsx +0 -12
  115. package/dist/components/form/transfer-list.jsx +0 -39
  116. package/dist/components/index.js +0 -45
  117. package/dist/components/page-calendar/calendar-header.jsx +0 -81
  118. package/dist/components/page-calendar/day-view.jsx +0 -87
  119. package/dist/components/page-calendar/event-pill.jsx +0 -25
  120. package/dist/components/page-calendar/index.js +0 -2
  121. package/dist/components/page-calendar/month-view.jsx +0 -47
  122. package/dist/components/page-calendar/page-calendar.jsx +0 -41
  123. package/dist/components/page-calendar/page-calendar.types.js +0 -1
  124. package/dist/components/page-calendar/page-calendar.utils.js +0 -71
  125. package/dist/components/page-calendar/week-view.jsx +0 -64
  126. package/dist/components/table/filter.jsx +0 -141
  127. package/dist/components/table/group.jsx +0 -68
  128. package/dist/components/table/index.jsx +0 -60
  129. package/dist/components/table/inner-table.jsx +0 -104
  130. package/dist/components/table/metadata.jsx +0 -36
  131. package/dist/components/table/pagination.jsx +0 -73
  132. package/dist/components/table/row.jsx +0 -58
  133. package/dist/components/table/sort.jsx +0 -105
  134. package/dist/components/table/table-lib.js +0 -83
  135. package/dist/components/table/table.context.jsx +0 -4
  136. package/dist/components/table/thead.jsx +0 -103
  137. package/dist/config/context.js +0 -12
  138. package/dist/config/default-translations.jsx +0 -83
  139. package/dist/config/default-tweaks.js +0 -4
  140. package/dist/constants.js +0 -2
  141. package/dist/hooks/use-click-outside.js +0 -17
  142. package/dist/hooks/use-color-parser.js +0 -9
  143. package/dist/hooks/use-components-provider.jsx +0 -19
  144. package/dist/hooks/use-debounce.js +0 -12
  145. package/dist/hooks/use-floating-ref.js +0 -6
  146. package/dist/hooks/use-form.js +0 -550
  147. package/dist/hooks/use-hover.js +0 -18
  148. package/dist/hooks/use-input-id.js +0 -5
  149. package/dist/hooks/use-is-coarse-device.js +0 -12
  150. package/dist/hooks/use-locale.js +0 -10
  151. package/dist/hooks/use-media-query.js +0 -25
  152. package/dist/hooks/use-on-event.js +0 -7
  153. package/dist/hooks/use-parent.js +0 -21
  154. package/dist/hooks/use-preferences.js +0 -23
  155. package/dist/hooks/use-previous.js +0 -9
  156. package/dist/hooks/use-reactive.js +0 -9
  157. package/dist/hooks/use-remove-scroll.js +0 -61
  158. package/dist/hooks/use-resize-observer.js +0 -17
  159. package/dist/hooks/use-stable-ref.js +0 -9
  160. package/dist/hooks/use-swipe.js +0 -17
  161. package/dist/hooks/use-translations.js +0 -9
  162. package/dist/hooks/use-tweaks.js +0 -9
  163. package/dist/hooks/use-window-size.js +0 -14
  164. package/dist/lib/combi-keys.js +0 -60
  165. package/dist/lib/dict.js +0 -39
  166. package/dist/lib/dom.js +0 -62
  167. package/dist/lib/fns.js +0 -46
  168. package/dist/lib/fzf.js +0 -117
  169. package/dist/lib/keyboard-area.js +0 -14
  170. package/dist/styles/common.js +0 -29
  171. package/dist/styles/dark.js +0 -214
  172. package/dist/styles/design-tokens.js +0 -69
  173. package/dist/styles/light.js +0 -214
  174. package/dist/styles/theme.js +0 -4
  175. package/dist/styles/theme.types.js +0 -1
  176. package/dist/types.js +0 -1
@@ -0,0 +1,145 @@
1
+ ---
2
+ title: Progress
3
+ description: Accessible progress bar built on Base UI Progress with optional label overlay and smooth transitions.
4
+ package: "@g4rcez/components"
5
+ export: "{ Progress }"
6
+ import: "import { Progress } from '@g4rcez/components'"
7
+ category: display
8
+ ---
9
+
10
+ # Progress
11
+
12
+ Accessible progress bar built on Base UI Progress with optional label overlay and smooth transitions.
13
+
14
+ ## Import
15
+
16
+ ```tsx
17
+ import { Progress } from "@g4rcez/components";
18
+ ```
19
+
20
+ ## Props
21
+
22
+ | Prop | Type | Default | Description |
23
+ |------|------|---------|-------------|
24
+ | `percent` | `number` | — | Current progress value (0–100) |
25
+ | `max` | `number` | — | Maximum value (forwarded to Base UI `Progress.Root`) |
26
+ | `label` | `Label` | — | Custom text overlay; overrides the default `{percent} %` |
27
+ | `container` | `string` | — | Additional classes for the track element |
28
+ | `className` | `string` | — | Additional classes for the indicator (fill) element |
29
+ | `textClassName` | `string` | — | Additional classes for the label text overlay |
30
+
31
+ ## Design Tokens
32
+
33
+ Tokens this component reads. Customize by overriding these CSS variables in your theme.
34
+
35
+ | Token | CSS Variable | Purpose |
36
+ |-------|-------------|---------|
37
+ | `bg-background` | `--background` | Track (unfilled) background |
38
+ | `bg-primary` | `--primary` | Indicator (fill) color |
39
+ | `text-primary-foreground` | `--primary-foreground` | Default label text color |
40
+
41
+ ## Examples
42
+
43
+ ### Basic Progress Bar
44
+
45
+ ```tsx
46
+ <Progress percent={75} />
47
+ ```
48
+
49
+ ### Animated Progress
50
+
51
+ ```tsx
52
+ function AnimatedProgress() {
53
+ const [progress, setProgress] = useState(0);
54
+
55
+ useEffect(() => {
56
+ const timer = setInterval(() => {
57
+ setProgress((prev) => (prev >= 100 ? 0 : prev + 10));
58
+ }, 500);
59
+ return () => clearInterval(timer);
60
+ }, []);
61
+
62
+ return <Progress percent={progress} />;
63
+ }
64
+ ```
65
+
66
+ ### Custom Label
67
+
68
+ ```tsx
69
+ <Progress percent={60} label="Uploading file… 60%" />
70
+ ```
71
+
72
+ ### Indeterminate / Unknown Duration
73
+
74
+ When `percent` is `undefined` the indicator is not rendered and the label is hidden, leaving only the track. Combine with a separate `Spinner` for unknown-duration operations.
75
+
76
+ ```tsx
77
+ {isLoading ? <Spinner /> : <Progress percent={uploadPercent} />}
78
+ ```
79
+
80
+ ### Multi-Step Form Progress
81
+
82
+ ```tsx
83
+ function MultiStepForm() {
84
+ const [currentStep, setCurrentStep] = useState(1);
85
+ const totalSteps = 4;
86
+ const progress = (currentStep / totalSteps) * 100;
87
+
88
+ return (
89
+ <div className="space-y-4">
90
+ <div className="flex justify-between items-center text-sm text-muted-foreground">
91
+ <span>Step {currentStep} of {totalSteps}</span>
92
+ <span>{Math.round(progress)}% complete</span>
93
+ </div>
94
+ <Progress percent={progress} />
95
+ </div>
96
+ );
97
+ }
98
+ ```
99
+
100
+ ### Upload with Status Label
101
+
102
+ ```tsx
103
+ function FileUploadProgress({ fileName, percent }: { fileName: string; percent: number }) {
104
+ const isDone = percent >= 100;
105
+
106
+ return (
107
+ <div className="space-y-1">
108
+ <div className="flex justify-between text-sm">
109
+ <span className="text-foreground">{fileName}</span>
110
+ <span className="text-muted-foreground">{percent}%</span>
111
+ </div>
112
+ <Progress
113
+ percent={percent}
114
+ label={isDone ? "Complete" : undefined}
115
+ container={isDone ? "bg-success/20" : undefined}
116
+ className={isDone ? "bg-success" : undefined}
117
+ />
118
+ </div>
119
+ );
120
+ }
121
+ ```
122
+
123
+ ## Do
124
+
125
+ - Use `Progress` when the duration of an operation is known or can be estimated.
126
+ - Provide a `label` when a percentage alone is not descriptive enough.
127
+ - Use `container` and `className` with design-token classes to change the track and fill colors (`bg-success`, `bg-warn`, etc.).
128
+
129
+ ## Don't
130
+
131
+ - Don't pass raw Tailwind color classes (`bg-green-500`, `bg-blue-500`) in `container` or `className` — use design tokens (`bg-success`, `bg-primary`) instead.
132
+ - Don't use arbitrary Tailwind values (`bg-[#abc]`) — override CSS variables in your `@theme` block.
133
+ - Don't use `Progress` for operations with unknown durations — use `Spinner` instead.
134
+ - Don't update `percent` more frequently than needed; excessive updates cause jitter.
135
+
136
+ ## Accessibility
137
+
138
+ - Built on Base UI `Progress.Root` which renders the correct `role="progressbar"` with `aria-valuenow`, `aria-valuemin`, and `aria-valuemax` automatically.
139
+ - The label overlay is a `<p>` element with `tabular-nums` for consistent digit rendering.
140
+
141
+ ## Notes
142
+
143
+ - The indicator moves via a CSS `translateX` transform (`translateX(-${100 - percent}%)`) for GPU-accelerated animation.
144
+ - The 500 ms `transition-transform ease-in-out` is applied via the `className` on the indicator element and can be overridden.
145
+ - When `percent` is `undefined` or `null`, the label and fill are hidden; the track remains visible.
@@ -0,0 +1,128 @@
1
+ ---
2
+ title: Radiobox
3
+ description: Styled radio button for selecting one option from a mutually exclusive set.
4
+ package: "@g4rcez/components"
5
+ export: "{ Radiobox }"
6
+ import: "import { Radiobox } from '@g4rcez/components/radiobox'"
7
+ category: form
8
+ ---
9
+
10
+ # Radiobox
11
+
12
+ Styled radio button for selecting one option from a mutually exclusive set.
13
+
14
+ ## Import
15
+
16
+ ```tsx
17
+ import { Radiobox } from "@g4rcez/components/radiobox";
18
+ ```
19
+
20
+ ## Props
21
+
22
+ Accepts all standard HTML `input[type="radio"]` attributes, plus:
23
+
24
+ | Prop | Type | Default | Description |
25
+ |------|------|---------|-------------|
26
+ | `children` | `React.ReactNode` | — | Label text or element displayed next to the radio button. |
27
+ | `size` | `"medium" \| "large"` | `"medium"` | Visual size of the radio button. |
28
+ | `className` | `string` | — | Additional CSS classes for the `<input>` element. |
29
+
30
+ ## Design Tokens
31
+
32
+ Tokens this component reads. Customize by overriding these CSS variables in your theme.
33
+
34
+ | Token | CSS Variable | Purpose |
35
+ |-------|-------------|---------|
36
+ | `border-card-border` | `--card-border` | Default border color of the radio circle |
37
+ | `text-primary` | `--primary` | Checked fill color (via `accent-color`) |
38
+ | `focus:ring-primary` | `--primary` | Focus ring color |
39
+ | `disabled:opacity-70` | — | Reduced opacity for disabled state |
40
+
41
+ ## Examples
42
+
43
+ ### Basic group
44
+
45
+ ```tsx
46
+ import { Radiobox } from "@g4rcez/components/radiobox";
47
+
48
+ export default function PlanSelector() {
49
+ return (
50
+ <div className="flex flex-col gap-sm">
51
+ <Radiobox name="plan" value="basic" defaultChecked>
52
+ Basic Plan
53
+ </Radiobox>
54
+ <Radiobox name="plan" value="pro">
55
+ Pro Plan
56
+ </Radiobox>
57
+ <Radiobox name="plan" value="enterprise">
58
+ Enterprise Plan
59
+ </Radiobox>
60
+ </div>
61
+ );
62
+ }
63
+ ```
64
+
65
+ ### Disabled state
66
+
67
+ ```tsx
68
+ import { Radiobox } from "@g4rcez/components/radiobox";
69
+
70
+ export default function DisabledOption() {
71
+ return (
72
+ <Radiobox disabled name="option" value="legacy">
73
+ Legacy (unavailable)
74
+ </Radiobox>
75
+ );
76
+ }
77
+ ```
78
+
79
+ ### In a grid layout
80
+
81
+ ```tsx
82
+ import { Radiobox } from "@g4rcez/components/radiobox";
83
+
84
+ export default function GenderSelector() {
85
+ return (
86
+ <div className="grid grid-cols-2 gap-base">
87
+ <Radiobox name="gender" value="male">Male</Radiobox>
88
+ <Radiobox name="gender" value="female">Female</Radiobox>
89
+ <Radiobox name="gender" value="non-binary">Non-binary</Radiobox>
90
+ <Radiobox name="gender" value="prefer-not">Prefer not to say</Radiobox>
91
+ </div>
92
+ );
93
+ }
94
+ ```
95
+
96
+ ## Do
97
+
98
+ - Group related `Radiobox` components by giving them the same `name` attribute.
99
+ - Provide a clear, concise label for each radio button via `children`.
100
+ - Use `Radiobox` when there are 2–7 mutually exclusive options; for more, prefer `Select`.
101
+ - Use design-token classes on wrapper elements (`bg-background`, `text-foreground`).
102
+
103
+ ## Don't
104
+
105
+ - Don't use `Radiobox` for independent toggles — use `Checkbox` instead.
106
+ - Don't use `Radiobox` for a simple yes/no choice — consider `Switch`.
107
+ - Don't pass raw Tailwind color classes (`text-blue-500`, `border-gray-300`) — use design tokens instead.
108
+ - Don't use arbitrary Tailwind values (`text-[--my-var]`) — override CSS variables in your `@theme` block instead.
109
+
110
+ ## Accessibility
111
+
112
+ - A semantic `<label>` wraps the `<input>`, associating label text with the radio button without extra `htmlFor` wiring.
113
+ - `aria-disabled` is set on the wrapper `<label>` when the input is disabled.
114
+ - Standard keyboard interaction: Arrow keys navigate between radios in the same group; Space selects.
115
+ - The `data-[disabled=true]:cursor-not-allowed` class gives a clear cursor signal for disabled items.
116
+
117
+ ## Data Attributes
118
+
119
+ | Attribute | Element | Value | Description |
120
+ |-----------|---------|-------|-------------|
121
+ | `data-component` | `label` | `"radiobox"` | Identifies the component. |
122
+ | `data-disabled` | `label` | `true \| undefined` | Set when the radio is disabled. |
123
+
124
+ ## Notes
125
+
126
+ - Uses the `form-radio` utility class for consistent cross-browser appearance.
127
+ - The `size` prop is accepted in the type definition but visual differentiation is handled via the `className` pass-through — you may add `h-5 w-5` or similar token-based size classes as needed.
128
+ - Render multiple `Radiobox` components inside a `<fieldset>` with a `<legend>` for full semantic grouping.
@@ -0,0 +1,138 @@
1
+ ---
2
+ title: RenderOnView
3
+ description: A performance wrapper that defers rendering children until the container enters the viewport.
4
+ package: "@g4rcez/components"
5
+ export: "{ RenderOnView }"
6
+ import: "import { RenderOnView } from '@g4rcez/components'"
7
+ category: core
8
+ ---
9
+
10
+ # RenderOnView
11
+
12
+ A performance wrapper that defers rendering its children until the container element enters the viewport. Uses the `IntersectionObserver` API and renders children exactly once — they stay mounted after first appearing.
13
+
14
+ ## Import
15
+
16
+ ```tsx
17
+ import { RenderOnView } from "@g4rcez/components";
18
+ ```
19
+
20
+ ## Props
21
+
22
+ | Prop | Type | Default | Description |
23
+ |------|------|---------|-------------|
24
+ | `onIntersection` | `() => void` | - | Callback fired once when the container first enters the viewport |
25
+ | `children` | `React.ReactNode` | - | Content to render when the container is visible |
26
+ | `as` | `React.ElementType` | `"div"` | HTML element to render the container as |
27
+ | `...props` | `React.ComponentPropsWithoutRef<T>` | - | All props valid for the chosen element type |
28
+
29
+ ## Design Tokens
30
+
31
+ None — `RenderOnView` is a layout/performance primitive that applies no styles of its own.
32
+
33
+ ## How It Works
34
+
35
+ 1. **Initial state**: The container element renders immediately but children are not mounted.
36
+ 2. **Intersection Observer**: A `useLayoutEffect` sets up an `IntersectionObserver` on the container.
37
+ 3. **First intersection**: When the container enters the viewport, `shouldRender` flips to `true` and children mount.
38
+ 4. **Stays mounted**: Children remain in the DOM after the first intersection — scrolling back out does not unmount them.
39
+ 5. **Callback**: `onIntersection` fires once at the moment of the first intersection.
40
+
41
+ ## Examples
42
+
43
+ ### Basic Lazy Rendering
44
+
45
+ ```tsx
46
+ <div>
47
+ <div className="h-screen flex items-center justify-center text-foreground">
48
+ Scroll down to see lazy content
49
+ </div>
50
+
51
+ <RenderOnView>
52
+ <ExpensiveChart data={largeDataset} />
53
+ </RenderOnView>
54
+ </div>
55
+ ```
56
+
57
+ ### With Intersection Callback
58
+
59
+ ```tsx
60
+ const TrackableSection = ({ sectionName, children }: { sectionName: string; children: React.ReactNode }) => (
61
+ <RenderOnView
62
+ onIntersection={() => analytics.track("Section Viewed", { section: sectionName })}
63
+ >
64
+ {children}
65
+ </RenderOnView>
66
+ );
67
+ ```
68
+
69
+ ### Multiple Deferred Sections
70
+
71
+ ```tsx
72
+ <main>
73
+ <HeroSection />
74
+
75
+ <RenderOnView>
76
+ <FeaturesSection />
77
+ </RenderOnView>
78
+
79
+ <RenderOnView>
80
+ <TestimonialsSection />
81
+ </RenderOnView>
82
+
83
+ <RenderOnView>
84
+ <ContactForm />
85
+ </RenderOnView>
86
+ </main>
87
+ ```
88
+
89
+ ### With Custom Element
90
+
91
+ ```tsx
92
+ <RenderOnView as="section" aria-label="Analytics charts">
93
+ <RevenueChart />
94
+ </RenderOnView>
95
+ ```
96
+
97
+ ### With React.lazy and Suspense
98
+
99
+ ```tsx
100
+ import { lazy, Suspense } from "react";
101
+ import { Spinner } from "@g4rcez/components";
102
+
103
+ const HeavyComponent = lazy(() => import("./HeavyComponent"));
104
+
105
+ <RenderOnView>
106
+ <Suspense fallback={<Spinner />}>
107
+ <HeavyComponent />
108
+ </Suspense>
109
+ </RenderOnView>
110
+ ```
111
+
112
+ ## Do
113
+
114
+ - Use `RenderOnView` for components with significant rendering cost (charts, maps, rich editors)
115
+ - Combine with `React.lazy` and `Suspense` for maximum bundle and rendering savings
116
+ - Use `as="section"` or another semantic element when the container has meaningful structure
117
+ - Use design-token classes on wrapper divs inside children (`bg-background`, `border-border`)
118
+
119
+ ## Don't
120
+
121
+ - Don't use `RenderOnView` for above-the-fold content — it adds an unnecessary observer
122
+ - Don't rely on it for critical content that must be in the initial HTML (SEO, LCP elements)
123
+ - Don't pass raw Tailwind color classes (`bg-white`, `text-gray-800`) to children or wrappers — use design tokens
124
+ - Don't use arbitrary Tailwind values (`bg-[#abc]`) — override CSS variables in your `@theme` block
125
+
126
+ ## Accessibility
127
+
128
+ - The container element is always rendered and present in the DOM
129
+ - Children mount only when visible, so screen readers will encounter them when they scroll into view
130
+ - Ensure any interactive content inside has proper focus management after mounting
131
+
132
+ ## Notes
133
+
134
+ - Children render exactly once and are never unmounted, regardless of subsequent scroll position
135
+ - `onIntersection` fires at most once per component lifetime
136
+ - The `IntersectionObserver` is disconnected on component unmount
137
+ - Uses `useLayoutEffect` for synchronous observer setup to avoid a flash of empty container
138
+ - SSR-safe: the initial `shouldRender` state starts as `false` since `ref.current` is `null` on the server
@@ -0,0 +1,159 @@
1
+ ---
2
+ title: Resizable
3
+ description: A wrapper that animates its height smoothly when content dimensions change.
4
+ package: "@g4rcez/components"
5
+ export: "{ Resizable }"
6
+ import: "import { Resizable } from '@g4rcez/components'"
7
+ category: core
8
+ ---
9
+
10
+ # Resizable
11
+
12
+ A wrapper that automatically animates its height whenever the inner content size changes. Uses `ResizeObserver` to detect dimension changes and `motion/react` to drive smooth height transitions.
13
+
14
+ ## Import
15
+
16
+ ```tsx
17
+ import { Resizable } from "@g4rcez/components";
18
+ ```
19
+
20
+ ## Props
21
+
22
+ | Prop | Type | Default | Description |
23
+ |------|------|---------|-------------|
24
+ | `children` | `React.ReactNode` | - | Content rendered inside the animated container |
25
+
26
+ ## Design Tokens
27
+
28
+ None — `Resizable` is a layout animation primitive that applies no color or spacing tokens.
29
+
30
+ ## How It Works
31
+
32
+ 1. An inner `<div>` holds the children and is observed by a `ResizeObserver`.
33
+ 2. When the inner `div`'s height changes, the observed height value updates via a motion value.
34
+ 3. An outer `motion.div` animates from the previous height to the new height using `motion/react`.
35
+ 4. While the content has no measured height yet (`h === 0`), the container uses `height: "auto"`.
36
+
37
+ ## Examples
38
+
39
+ ### Collapsible Content
40
+
41
+ ```tsx
42
+ import { useState } from "react";
43
+
44
+ const CollapsiblePanel = () => {
45
+ const [isOpen, setIsOpen] = useState(false);
46
+
47
+ return (
48
+ <div className="border border-border rounded-card">
49
+ <button
50
+ type="button"
51
+ className="w-full px-4 py-3 text-left font-medium text-foreground"
52
+ onClick={() => setIsOpen((v) => !v)}
53
+ >
54
+ Toggle Details
55
+ </button>
56
+ <Resizable>
57
+ {isOpen ? (
58
+ <div className="px-4 pb-4 text-foreground">
59
+ <p>This content expands and collapses with a smooth height animation.</p>
60
+ <p>Additional paragraphs will also animate smoothly.</p>
61
+ </div>
62
+ ) : null}
63
+ </Resizable>
64
+ </div>
65
+ );
66
+ };
67
+ ```
68
+
69
+ ### Dynamic List
70
+
71
+ ```tsx
72
+ import { useState } from "react";
73
+ import { Button } from "@g4rcez/components/button";
74
+
75
+ const GrowingList = () => {
76
+ const [items, setItems] = useState(["Item 1"]);
77
+
78
+ return (
79
+ <div className="flex flex-col gap-base">
80
+ <Button
81
+ theme="primary"
82
+ onClick={() => setItems((prev) => [...prev, `Item ${prev.length + 1}`])}
83
+ >
84
+ Add Item
85
+ </Button>
86
+ <Resizable>
87
+ <ul className="flex flex-col gap-sm">
88
+ {items.map((item) => (
89
+ <li key={item} className="text-foreground">
90
+ {item}
91
+ </li>
92
+ ))}
93
+ </ul>
94
+ </Resizable>
95
+ </div>
96
+ );
97
+ };
98
+ ```
99
+
100
+ ### Animated Tab Panels
101
+
102
+ ```tsx
103
+ const TabbedContent = ({ activeTab }: { activeTab: string }) => (
104
+ <Resizable>
105
+ <div className="p-4 text-foreground">
106
+ {activeTab === "overview" && <OverviewPanel />}
107
+ {activeTab === "settings" && <SettingsPanel />}
108
+ {activeTab === "history" && <HistoryPanel />}
109
+ </div>
110
+ </Resizable>
111
+ );
112
+ ```
113
+
114
+ ### Async Content Loading
115
+
116
+ ```tsx
117
+ const AsyncCard = ({ data }: { data: string[] | null }) => (
118
+ <div className="rounded-card border border-border bg-card-background shadow-shadow-card">
119
+ <Resizable>
120
+ {data === null ? (
121
+ <div className="p-4 text-muted-foreground">Loading…</div>
122
+ ) : (
123
+ <ul className="p-4 flex flex-col gap-sm">
124
+ {data.map((item) => (
125
+ <li key={item} className="text-foreground">{item}</li>
126
+ ))}
127
+ </ul>
128
+ )}
129
+ </Resizable>
130
+ </div>
131
+ );
132
+ ```
133
+
134
+ ## Do
135
+
136
+ - Use `Resizable` around any content that changes height to provide a polished animation
137
+ - Wrap only the specific dynamic section — not large, stable portions of the page
138
+ - Apply visual styles (`bg-*`, `border-*`, `rounded-*`) to elements inside `Resizable`, not to `Resizable` itself
139
+ - Use design-token classes for all styling (`bg-background`, `border-border`, `rounded-card`)
140
+
141
+ ## Don't
142
+
143
+ - Don't use `Resizable` on content that changes height at very high frequency (e.g., per-frame updates) — it may cause performance issues
144
+ - Don't wrap large, stable sections of a page — keep the observed area small
145
+ - Don't pass raw Tailwind color classes (`bg-white`, `border-gray-200`) inside `Resizable` wrappers — use design tokens
146
+ - Don't use arbitrary Tailwind values (`bg-[#fff]`) — override CSS variables in your `@theme` block
147
+
148
+ ## Accessibility
149
+
150
+ - `Resizable` is a layout animation wrapper and introduces no semantic elements or ARIA attributes
151
+ - Ensure content inside maintains correct focus order and accessible roles
152
+ - For accordion/collapsible patterns, add `aria-expanded` and `aria-controls` to the trigger button
153
+
154
+ ## Notes
155
+
156
+ - Requires `motion/react` as a peer dependency
157
+ - The outer `motion.div` drives the height animation; the inner `div` is the measured reference
158
+ - SSR-safe: `isSsr()` prevents `ResizeObserver` from being created on the server
159
+ - The component is a client component (`"use client"`)