remix 3.0.0-rc.4 → 3.0.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 (191) hide show
  1. package/INDEX.md +6 -21
  2. package/README.md +1 -1
  3. package/dist/component/dev/refresh.d.ts +2 -0
  4. package/dist/component/dev/refresh.d.ts.map +1 -0
  5. package/dist/{ui → component}/dev/refresh.js +1 -1
  6. package/dist/component/jsx-dev-runtime.d.ts +2 -0
  7. package/dist/component/jsx-dev-runtime.d.ts.map +1 -0
  8. package/dist/{ui → component}/jsx-dev-runtime.js +1 -1
  9. package/dist/component/jsx-runtime.d.ts +2 -0
  10. package/dist/component/jsx-runtime.d.ts.map +1 -0
  11. package/dist/{ui → component}/jsx-runtime.js +1 -1
  12. package/dist/component/server.d.ts +2 -0
  13. package/dist/component/server.d.ts.map +1 -0
  14. package/dist/{ui → component}/server.js +1 -1
  15. package/dist/component/test.d.ts +2 -0
  16. package/dist/component/test.d.ts.map +1 -0
  17. package/dist/{ui → component}/test.js +1 -1
  18. package/dist/component-hmr/assets.d.ts +2 -0
  19. package/dist/component-hmr/assets.d.ts.map +1 -0
  20. package/dist/{ui-hmr.js → component-hmr/assets.js} +1 -1
  21. package/dist/component-hmr/node.d.ts +3 -0
  22. package/dist/component-hmr/node.d.ts.map +1 -0
  23. package/dist/{ui-hmr → component-hmr}/node.js +1 -1
  24. package/dist/component-hmr/runtime/browser.d.ts +2 -0
  25. package/dist/component-hmr/runtime/browser.d.ts.map +1 -0
  26. package/dist/{ui-hmr → component-hmr}/runtime/browser.js +1 -1
  27. package/dist/component-hmr/runtime/server.d.ts +2 -0
  28. package/dist/component-hmr/runtime/server.d.ts.map +1 -0
  29. package/dist/{ui-hmr → component-hmr}/runtime/server.js +1 -1
  30. package/dist/component-hmr.d.ts +2 -0
  31. package/dist/component-hmr.d.ts.map +1 -0
  32. package/dist/{ui-hmr/assets.js → component-hmr.js} +1 -1
  33. package/dist/component.d.ts +2 -0
  34. package/dist/component.d.ts.map +1 -0
  35. package/dist/{ui/anchor.js → component.js} +1 -1
  36. package/guides/01-start-here.md +20 -20
  37. package/guides/02-routing-and-controllers.md +1 -1
  38. package/guides/03-request-handling.md +1 -1
  39. package/guides/04-rendering-ui.md +41 -307
  40. package/guides/05-interactivity.md +42 -37
  41. package/guides/06-streaming-ui-with-frames.md +28 -70
  42. package/guides/07-animation.md +41 -35
  43. package/guides/08-data-and-validation.md +423 -27
  44. package/guides/09-forms-and-mutations.md +250 -25
  45. package/guides/13-testing.md +10 -10
  46. package/guides/14-cli-and-tooling.md +2 -2
  47. package/package.json +96 -180
  48. package/src/assets/README.md +12 -12
  49. package/src/cli/README.md +3 -3
  50. package/src/{ui → component}/README.md +31 -75
  51. package/src/{ui → component}/dev/refresh.ts +1 -1
  52. package/src/{ui → component}/jsx-dev-runtime.ts +1 -1
  53. package/src/{ui → component}/jsx-runtime.ts +1 -1
  54. package/src/{ui → component}/server/README.md +8 -8
  55. package/src/{ui → component}/server.ts +1 -1
  56. package/src/{ui → component}/test/README.md +6 -6
  57. package/src/{ui → component}/test.ts +1 -1
  58. package/src/{ui-hmr → component-hmr}/README.md +13 -13
  59. package/src/{ui-hmr → component-hmr}/assets.ts +1 -1
  60. package/src/{ui-hmr → component-hmr}/node.ts +1 -1
  61. package/src/{ui-hmr → component-hmr}/runtime/browser.ts +1 -1
  62. package/src/{ui-hmr → component-hmr}/runtime/server.ts +1 -1
  63. package/{dist/ui/accordion.js → src/component-hmr.ts} +1 -1
  64. package/{dist/ui/tabs.js → src/component.ts} +1 -1
  65. package/src/data-table/README.md +2 -1
  66. package/src/multiple-import-maps-polyfill/README.md +3 -3
  67. package/src/node-hmr/README.md +6 -6
  68. package/src/render-middleware/README.md +5 -5
  69. package/src/spa/README.md +4 -4
  70. package/src/test/README.md +4 -4
  71. package/dist/ui/accordion/primitives.d.ts +0 -2
  72. package/dist/ui/accordion/primitives.d.ts.map +0 -1
  73. package/dist/ui/accordion/primitives.js +0 -2
  74. package/dist/ui/accordion.d.ts +0 -2
  75. package/dist/ui/accordion.d.ts.map +0 -1
  76. package/dist/ui/anchor.d.ts +0 -2
  77. package/dist/ui/anchor.d.ts.map +0 -1
  78. package/dist/ui/animation.d.ts +0 -2
  79. package/dist/ui/animation.d.ts.map +0 -1
  80. package/dist/ui/animation.js +0 -2
  81. package/dist/ui/breadcrumbs.d.ts +0 -2
  82. package/dist/ui/breadcrumbs.d.ts.map +0 -1
  83. package/dist/ui/breadcrumbs.js +0 -2
  84. package/dist/ui/button.d.ts +0 -3
  85. package/dist/ui/button.d.ts.map +0 -1
  86. package/dist/ui/button.js +0 -3
  87. package/dist/ui/checkbox.d.ts +0 -3
  88. package/dist/ui/checkbox.d.ts.map +0 -1
  89. package/dist/ui/checkbox.js +0 -3
  90. package/dist/ui/combobox/primitives.d.ts +0 -2
  91. package/dist/ui/combobox/primitives.d.ts.map +0 -1
  92. package/dist/ui/combobox/primitives.js +0 -2
  93. package/dist/ui/combobox.d.ts +0 -2
  94. package/dist/ui/combobox.d.ts.map +0 -1
  95. package/dist/ui/combobox.js +0 -2
  96. package/dist/ui/dev/refresh.d.ts +0 -2
  97. package/dist/ui/dev/refresh.d.ts.map +0 -1
  98. package/dist/ui/input.d.ts +0 -3
  99. package/dist/ui/input.d.ts.map +0 -1
  100. package/dist/ui/input.js +0 -3
  101. package/dist/ui/jsx-dev-runtime.d.ts +0 -2
  102. package/dist/ui/jsx-dev-runtime.d.ts.map +0 -1
  103. package/dist/ui/jsx-runtime.d.ts +0 -2
  104. package/dist/ui/jsx-runtime.d.ts.map +0 -1
  105. package/dist/ui/listbox.d.ts +0 -2
  106. package/dist/ui/listbox.d.ts.map +0 -1
  107. package/dist/ui/listbox.js +0 -2
  108. package/dist/ui/menu/primitives.d.ts +0 -2
  109. package/dist/ui/menu/primitives.d.ts.map +0 -1
  110. package/dist/ui/menu/primitives.js +0 -2
  111. package/dist/ui/menu.d.ts +0 -2
  112. package/dist/ui/menu.d.ts.map +0 -1
  113. package/dist/ui/menu.js +0 -2
  114. package/dist/ui/popover.d.ts +0 -2
  115. package/dist/ui/popover.d.ts.map +0 -1
  116. package/dist/ui/popover.js +0 -2
  117. package/dist/ui/radio.d.ts +0 -3
  118. package/dist/ui/radio.d.ts.map +0 -1
  119. package/dist/ui/radio.js +0 -3
  120. package/dist/ui/select/primitives.d.ts +0 -2
  121. package/dist/ui/select/primitives.d.ts.map +0 -1
  122. package/dist/ui/select/primitives.js +0 -2
  123. package/dist/ui/select.d.ts +0 -2
  124. package/dist/ui/select.d.ts.map +0 -1
  125. package/dist/ui/select.js +0 -2
  126. package/dist/ui/server.d.ts +0 -2
  127. package/dist/ui/server.d.ts.map +0 -1
  128. package/dist/ui/tabs/primitives.d.ts +0 -2
  129. package/dist/ui/tabs/primitives.d.ts.map +0 -1
  130. package/dist/ui/tabs/primitives.js +0 -2
  131. package/dist/ui/tabs.d.ts +0 -2
  132. package/dist/ui/tabs.d.ts.map +0 -1
  133. package/dist/ui/test.d.ts +0 -2
  134. package/dist/ui/test.d.ts.map +0 -1
  135. package/dist/ui/toggle/primitives.d.ts +0 -2
  136. package/dist/ui/toggle/primitives.d.ts.map +0 -1
  137. package/dist/ui/toggle/primitives.js +0 -2
  138. package/dist/ui/toggle.d.ts +0 -3
  139. package/dist/ui/toggle.d.ts.map +0 -1
  140. package/dist/ui/toggle.js +0 -3
  141. package/dist/ui-hmr/assets.d.ts +0 -2
  142. package/dist/ui-hmr/assets.d.ts.map +0 -1
  143. package/dist/ui-hmr/node.d.ts +0 -3
  144. package/dist/ui-hmr/node.d.ts.map +0 -1
  145. package/dist/ui-hmr/runtime/browser.d.ts +0 -2
  146. package/dist/ui-hmr/runtime/browser.d.ts.map +0 -1
  147. package/dist/ui-hmr/runtime/server.d.ts +0 -2
  148. package/dist/ui-hmr/runtime/server.d.ts.map +0 -1
  149. package/dist/ui-hmr.d.ts +0 -2
  150. package/dist/ui-hmr.d.ts.map +0 -1
  151. package/dist/ui.d.ts +0 -2
  152. package/dist/ui.d.ts.map +0 -1
  153. package/dist/ui.js +0 -2
  154. package/src/ui/accordion/README.md +0 -202
  155. package/src/ui/accordion/primitives.ts +0 -2
  156. package/src/ui/accordion.ts +0 -2
  157. package/src/ui/anchor/README.md +0 -196
  158. package/src/ui/anchor.ts +0 -2
  159. package/src/ui/animation/README.md +0 -318
  160. package/src/ui/animation.ts +0 -2
  161. package/src/ui/breadcrumbs/README.md +0 -55
  162. package/src/ui/breadcrumbs.ts +0 -2
  163. package/src/ui/button/README.md +0 -44
  164. package/src/ui/button.ts +0 -3
  165. package/src/ui/checkbox/README.md +0 -59
  166. package/src/ui/checkbox.ts +0 -3
  167. package/src/ui/combobox/README.md +0 -194
  168. package/src/ui/combobox/primitives.ts +0 -2
  169. package/src/ui/combobox.ts +0 -2
  170. package/src/ui/input/README.md +0 -52
  171. package/src/ui/input.ts +0 -3
  172. package/src/ui/listbox/README.md +0 -83
  173. package/src/ui/listbox.ts +0 -2
  174. package/src/ui/menu/README.md +0 -161
  175. package/src/ui/menu/primitives.ts +0 -2
  176. package/src/ui/menu.ts +0 -2
  177. package/src/ui/popover/README.md +0 -103
  178. package/src/ui/popover.ts +0 -2
  179. package/src/ui/radio/README.md +0 -53
  180. package/src/ui/radio.ts +0 -3
  181. package/src/ui/select/README.md +0 -117
  182. package/src/ui/select/primitives.ts +0 -2
  183. package/src/ui/select.ts +0 -2
  184. package/src/ui/tabs/README.md +0 -141
  185. package/src/ui/tabs/primitives.ts +0 -2
  186. package/src/ui/tabs.ts +0 -2
  187. package/src/ui/toggle/README.md +0 -56
  188. package/src/ui/toggle/primitives.ts +0 -2
  189. package/src/ui/toggle.ts +0 -3
  190. package/src/ui-hmr.ts +0 -2
  191. package/src/ui.ts +0 -2
@@ -68,8 +68,8 @@ export default createController(routes.albums, {
68
68
  Now the page can render that route with `Frame`:
69
69
 
70
70
  ```tsx filename=app/actions/albums/show-page.tsx
71
- import { Frame } from "remix/ui";
72
- import type { Handle } from "remix/ui";
71
+ import { Frame } from "remix/component";
72
+ import type { Handle } from "remix/component";
73
73
 
74
74
  import { routes } from "../../routes.ts";
75
75
  import { Document } from "../document.tsx";
@@ -148,7 +148,7 @@ response type. Normal HTML actions should use the standard `render()` middleware
148
148
  continue, such as an email preview or a small embedded fragment:
149
149
 
150
150
  ```tsx
151
- import { renderToString } from "remix/ui/server";
151
+ import { renderToString } from "remix/component/server";
152
152
 
153
153
  // After loading recommendations:
154
154
  let html = await renderToString(<AlbumRecommendations albums={recommendations} />);
@@ -160,62 +160,19 @@ frames with fallbacks arrive after the initial HTML.
160
160
  ## Resolve frames in the browser
161
161
 
162
162
  A blocking frame resolves before the browser receives the page. A frame with a fallback may still
163
- be pending, and any frame can reload later. Back in `app/actions/public/entry.ts`, add `resolveFrame`
164
- to the existing `run()` options for those requests:
163
+ be pending, and any frame can reload later. The `run()` call in `app/actions/public/entry.ts` already
164
+ provides a default browser resolver, so these requests need no additional configuration.
165
165
 
166
- ```ts filename=app/actions/public/entry.ts lines=[1,9-27,29-39]
167
- import type { ResolveFrameOptions } from "remix/ui";
168
- import { run } from "remix/ui";
166
+ The default resolver fetches same-origin HTML, follows redirects, preserves form submission methods
167
+ and encoding, and cancels requests when a frame is removed or its request is superseded. It also
168
+ keeps the redirect destination available so navigation can update the browser URL after a form saves.
169
169
 
170
- // ...
170
+ If your app needs different request or response handling, you can provide your own `resolveFrame`
171
+ function in the `run()` options. The [`remix/component` API overview](../src/component/README.md)
172
+ covers the callback's arguments and return values.
171
173
 
172
- let app = run({
173
- // ...
174
-
175
- async resolveFrame(src, options) {
176
- let headers = new Headers({ Accept: "text/html", "X-Remix-Frame": "true" });
177
- if (options?.target) headers.set("X-Remix-Target", options.target);
178
-
179
- let response = await fetch(src, {
180
- body: getRequestBody(options),
181
- credentials: "same-origin",
182
- headers,
183
- method: options?.method,
184
- mode: "same-origin",
185
- signal: options?.signal,
186
- });
187
-
188
- if (!response.ok) {
189
- throw new Error(`Frame request failed with status ${response.status}`);
190
- }
191
-
192
- return response.body ?? response.text();
193
- },
194
- });
195
-
196
- function getRequestBody(options?: ResolveFrameOptions): BodyInit | undefined {
197
- let formData = options?.formData;
198
- if (!formData) return;
199
- if (options.encType !== "application/x-www-form-urlencoded") return formData;
200
-
201
- let body = new URLSearchParams();
202
- for (let [name, value] of formData) {
203
- body.append(name, typeof value === "string" ? value : value.name);
204
- }
205
- return body;
206
- }
207
- ```
208
-
209
- The default resolver only makes same-origin requests, including redirects. This custom resolver
210
- keeps that restriction with `mode: "same-origin"`; omit it only when the application explicitly
211
- trusts a cross-origin source. Remix does not sanitize returned HTML before reconciling it into the
212
- current document, and same-origin user-generated HTML is not implicitly safe. Sanitize untrusted
213
- content before returning it.
214
-
215
- Pass `options.signal` to `fetch()` instead of letting a removed or superseded frame request
216
- continue. GET forms already include their values in `src`. For non-GET forms, the resolver receives
217
- the browser's `FormData`, method, and encoding. This example preserves URL-encoded and multipart
218
- form bodies; the resolver remains the place to apply other encoding or `_method` conventions.
174
+ Remix does not sanitize returned HTML before reconciling it into the current document, and
175
+ same-origin user-generated HTML is not implicitly safe. Sanitize untrusted content before returning it.
219
176
 
220
177
  ## Name and reload frames {#frames-and-partial-server-rendered-ui}
221
178
 
@@ -245,9 +202,8 @@ setup state and receive current server props.
245
202
 
246
203
  ## Navigate a frame with a form
247
204
 
248
- Once `run({ resolveFrame })` starts, an eligible same-origin form follows the same frame navigation
249
- path as a link. This GET form reloads the recommendations frame without a client entry or submit
250
- handler:
205
+ Once `run()` starts, an eligible same-origin form follows the same frame navigation path as a link.
206
+ This GET form reloads the recommendations frame without a client entry or submit handler:
251
207
 
252
208
  ```tsx
253
209
  <form
@@ -265,11 +221,12 @@ frame, `data-rmx-src` can provide a different request URL for that named frame,
265
221
  `data-rmx-reset-scroll="false"` preserves the current scroll position, and `data-rmx-document` opts out of
266
222
  interception. `data-rmx-history="push|replace"` controls how the navigation updates history.
267
223
 
268
- GET controls are already encoded in the destination URL. For non-GET forms, `resolveFrame` receives
269
- `formData`, `method`, and `encType`. The action should return HTML for the targeted frame when it
270
- receives a frame request, while keeping its normal document response or redirect for unenhanced
271
- submissions. Non-GET submissions to the current URL replace that history entry; GET submissions and
272
- submissions to a different URL push one. The `data-rmx-history` attribute overrides those defaults.
224
+ GET controls are already encoded in the destination URL. For non-GET forms, the default resolver
225
+ sends the form's body with its method and encoding. The action should return HTML for the targeted
226
+ frame when it receives a frame request, while keeping its normal document response or redirect for
227
+ unenhanced submissions. Non-GET submissions to the current URL replace that history entry; GET
228
+ submissions and submissions to a different URL push one. The `data-rmx-history` attribute overrides
229
+ those defaults.
273
230
 
274
231
  ## Coordinate a mutation and a separate frame reload {#coordinating-forms-fetches-frame-reloads-and-navigation}
275
232
 
@@ -277,7 +234,7 @@ Start with a form whose action works without browser JavaScript. A client entry
277
234
  same submission, send its `FormData`, and reload a different route after the action succeeds:
278
235
 
279
236
  ```tsx
280
- import { on } from "remix/ui";
237
+ import { on } from "remix/component";
281
238
 
282
239
  // Inside a client-entry component's render function:
283
240
  <form
@@ -308,7 +265,7 @@ import { on } from "remix/ui";
308
265
 
309
266
  This custom handler is useful because the mutation response and recommendations frame are two
310
267
  different requests. When the form action itself returns the HTML that belongs in the target frame,
311
- prefer `data-rmx-target` and let the runtime submit it through `resolveFrame`.
268
+ prefer `data-rmx-target` and let the runtime submit it.
312
269
 
313
270
  The handler above keeps the mutation in its existing action and the recommendations HTML in its
314
271
  existing `GET` action. The browser component coordinates the two requests without duplicating either
@@ -345,15 +302,16 @@ Add `data-rmx-history="replace"` when it should replace the current history entr
345
302
 
346
303
  ## Handle failures and cancellation
347
304
 
348
- The server renderer's `onError` and the browser app's `error` event are reporting hooks. The app's
349
- frame resolver decides whether a non-success response should become bounded HTML for that region or
350
- an error reported through those hooks.
305
+ The server renderer's `onError` and the browser app's `error` event are reporting hooks. The default
306
+ browser resolver renders HTML validation responses such as `400`, but reports server errors (`500`
307
+ and above) and non-HTML error responses through the browser app's `error` event.
351
308
 
352
309
  If a deferred frame fails after its fallback has been sent, the server cannot replace the whole page
353
310
  with a new error response. Keep the fallback useful, report the error, and let the surrounding page
354
311
  continue when that is safe.
355
312
 
356
- Pass `request.signal` through server frame work and the browser resolver's signal through `fetch()`.
313
+ Pass `request.signal` through server frame work. The default browser resolver forwards its cancellation
314
+ signal to `fetch()`; a custom resolver should do the same.
357
315
  [Errors and Cancellation](/errors-and-cancellation/) covers reporting policy, while
358
316
  [Production](/production/) covers disconnects, compression, and other deployment concerns.
359
317
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Animation
3
- description: The CSS-first animation model and Remix UI helpers for motion that respects rendering state.
3
+ description: The CSS-first animation model and Remix component helpers for motion that respects rendering state.
4
4
  ---
5
5
 
6
6
  ## CSS-first visual states {#css-first-visual-states}
@@ -8,8 +8,8 @@ description: The CSS-first animation model and Remix UI helpers for motion that
8
8
  Start with CSS when the browser already owns the state. Hover, focus, active, checked, open, selected, and reduced-motion states do not need JavaScript state unless your render output also depends on them.
9
9
 
10
10
  ```tsx filename=app/ui/pressable-card.tsx
11
- import { css } from "remix/ui";
12
- import type { Handle, RemixNode } from "remix/ui";
11
+ import { css } from "remix/component";
12
+ import type { Handle, RemixNode } from "remix/component";
13
13
 
14
14
  export function PressableCard(handle: Handle<{ children: RemixNode }>) {
15
15
  return () => (
@@ -42,10 +42,16 @@ const cardStyle = css({
42
42
 
43
43
  Use Remix animation helpers when the motion depends on rendering state: a node enters, a node exits, a keyed item moves, or an event needs an interruptible animation.
44
44
 
45
+ The animation helpers are provided by `@remix-run/ui`. This package is currently unstable and versioned independently. It is not available through the `remix` package, so install it separately:
46
+
47
+ ```sh
48
+ npm i remix @remix-run/ui
49
+ ```
50
+
45
51
  ```tsx
46
- import { createMixin, css, on } from "remix/ui";
47
- import type { Handle } from "remix/ui";
48
- import { spring } from "remix/ui/animation";
52
+ import { createMixin, css, on } from "remix/component";
53
+ import type { Handle } from "remix/component";
54
+ import { spring } from "@remix-run/ui/animation";
49
55
 
50
56
  export function PressStateDemo(handle: Handle) {
51
57
  let pressed = false;
@@ -123,9 +129,9 @@ const pressableStyles = css({
123
129
  `animateEntrance(...)` runs when a host node is inserted. `animateExit(...)` lets a removed node stay in the DOM until its exit animation finishes.
124
130
 
125
131
  ```tsx filename=app/ui/public/notice.tsx
126
- import { clientEntry, css, on } from "remix/ui";
127
- import { animateEntrance, animateExit, spring } from "remix/ui/animation";
128
- import type { Handle } from "remix/ui";
132
+ import { clientEntry, css, on } from "remix/component";
133
+ import { animateEntrance, animateExit, spring } from "@remix-run/ui/animation";
134
+ import type { Handle } from "remix/component";
129
135
 
130
136
  export const Notice = clientEntry(import.meta.url, function Notice(handle: Handle) {
131
137
  let visible = true;
@@ -180,9 +186,9 @@ Keep keys stable when toggling between related elements. A stable key tells Remi
180
186
  Pass `true` for the default opacity animation or `false` to disable a mixin without changing the surrounding `mix` array. `animateEntrance({ initial: false })` skips the first insertion for a key but still animates later insertions. If the same keyed element returns before its exit finishes, Remix reclaims that DOM node and animates it back toward its rendered styles.
181
187
 
182
188
  ```tsx
183
- import { css, on } from "remix/ui";
184
- import type { Handle } from "remix/ui";
185
- import { animateEntrance, animateExit, spring } from "remix/ui/animation";
189
+ import { css, on } from "remix/component";
190
+ import type { Handle } from "remix/component";
191
+ import { animateEntrance, animateExit, spring } from "@remix-run/ui/animation";
186
192
 
187
193
  export function NoticePresenceDemo(handle: Handle) {
188
194
  let visible = true;
@@ -257,9 +263,9 @@ const noticeStyles = css({
257
263
  `animateLayout(...)` measures a host node before and after a render, then animates the visual delta. This is the right tool for sorted lists, expanding cards, and elements that move because layout changed.
258
264
 
259
265
  ```tsx filename=app/ui/public/reorder-list.tsx
260
- import { clientEntry, css, on } from "remix/ui";
261
- import { animateLayout, spring } from "remix/ui/animation";
262
- import type { Handle } from "remix/ui";
266
+ import { clientEntry, css, on } from "remix/component";
267
+ import { animateLayout, spring } from "@remix-run/ui/animation";
268
+ import type { Handle } from "remix/component";
263
269
 
264
270
  const initialItems = ["Design", "Build", "Review"];
265
271
 
@@ -309,9 +315,9 @@ const itemStyle = css({
309
315
  Layout animation includes size projection by default. Pass `size: false` when only position should animate and scaling the element's contents would look wrong.
310
316
 
311
317
  ```tsx
312
- import { css } from "remix/ui";
313
- import type { Handle } from "remix/ui";
314
- import { animateLayout, spring } from "remix/ui/animation";
318
+ import { css } from "remix/component";
319
+ import type { Handle } from "remix/component";
320
+ import { animateLayout, spring } from "@remix-run/ui/animation";
315
321
 
316
322
  export function ReorderingDemo(handle: Handle) {
317
323
  let order = initialOrder;
@@ -388,9 +394,9 @@ const itemStyles = css({
388
394
  `spring()` returns an iterator decorated for CSS transitions and Web Animations. Stringify it in CSS, spread it into animation options, or iterate it for custom JavaScript animation.
389
395
 
390
396
  ```tsx filename=app/ui/public/bouncy-switch.tsx
391
- import { clientEntry, css, on } from "remix/ui";
392
- import { spring } from "remix/ui/animation";
393
- import type { Handle } from "remix/ui";
397
+ import { clientEntry, css, on } from "remix/component";
398
+ import { spring } from "@remix-run/ui/animation";
399
+ import type { Handle } from "remix/component";
394
400
 
395
401
  export const BouncySwitch = clientEntry(import.meta.url, function BouncySwitch(handle: Handle) {
396
402
  let enabled = true;
@@ -439,7 +445,7 @@ const thumbStyle = css({
439
445
  Use `tween(...)` when you need a time-based value loop rather than CSS or WAAPI timing.
440
446
 
441
447
  ```ts filename=app/ui/public/count-up.ts
442
- import { easings, tween } from "remix/ui/animation";
448
+ import { easings, tween } from "@remix-run/ui/animation";
443
449
 
444
450
  export function countUp(from: number, to: number, onValue: (value: number) => void) {
445
451
  let animation = tween({ from, to, duration: 300, curve: easings.easeOut });
@@ -461,10 +467,10 @@ export function countUp(from: number, to: number, onValue: (value: number) => vo
461
467
  Prefer CSS transitions and animation mixins for ordinary UI. Use `tween` for canvas, custom counters, or values that are not CSS properties.
462
468
 
463
469
  ```tsx
464
- import { css, on, ref } from "remix/ui";
465
- import type { Handle } from "remix/ui";
466
- import { spring } from "remix/ui/animation";
467
- import type { SpringPreset } from "remix/ui/animation";
470
+ import { css, on, ref } from "remix/component";
471
+ import type { Handle } from "remix/component";
472
+ import { spring } from "@remix-run/ui/animation";
473
+ import type { SpringPreset } from "@remix-run/ui/animation";
468
474
 
469
475
  import { dragVelocityEvents } from "./gallery/drag-release.ts";
470
476
 
@@ -681,9 +687,9 @@ const hintStyle = css({
681
687
  ```
682
688
 
683
689
  ```tsx
684
- import { css, on } from "remix/ui";
685
- import type { Handle } from "remix/ui";
686
- import { spring } from "remix/ui/animation";
690
+ import { css, on } from "remix/component";
691
+ import type { Handle } from "remix/component";
692
+ import { spring } from "@remix-run/ui/animation";
687
693
 
688
694
  export function BouncySwitchDemo(handle: Handle) {
689
695
  let isOn = true;
@@ -738,7 +744,7 @@ const thumbStyles = css({
738
744
  The UI package also keeps a gallery of smaller motion experiments. Keep this kind of broad demo in a bounded frame so it does not take over the surrounding guide page.
739
745
 
740
746
  ```tsx
741
- import { css, on, type Handle, type RemixNode } from "remix/ui";
747
+ import { css, on, type Handle, type RemixNode } from "remix/component";
742
748
  import { DefaultAnimate } from "./gallery/default-animate.tsx";
743
749
  import { EnterAnimation } from "./gallery/enter.tsx";
744
750
  import { ExitAnimation } from "./gallery/exit.tsx";
@@ -937,9 +943,9 @@ Event handlers receive abort signals, and animation mixins cancel or replace in-
937
943
  For custom imperative animations, keep the current animation in setup scope and cancel it before starting the next one:
938
944
 
939
945
  ```tsx filename=app/ui/public/ripple-button.tsx
940
- import { clientEntry, css, on, ref } from "remix/ui";
941
- import { spring } from "remix/ui/animation";
942
- import type { Handle } from "remix/ui";
946
+ import { clientEntry, css, on, ref } from "remix/component";
947
+ import { spring } from "@remix-run/ui/animation";
948
+ import type { Handle } from "remix/component";
943
949
 
944
950
  export const RippleButton = clientEntry(import.meta.url, function RippleButton(_handle: Handle) {
945
951
  let node: HTMLButtonElement;
@@ -984,8 +990,8 @@ CSS transitions are also interruptible: changing the target value while a transi
984
990
  Respect `prefers-reduced-motion` at the CSS boundary first. It works before JavaScript loads, applies to server-rendered frames, and covers static transitions.
985
991
 
986
992
  ```tsx filename=app/ui/motion-safe-panel.tsx
987
- import { css } from "remix/ui";
988
- import type { Handle, RemixNode } from "remix/ui";
993
+ import { css } from "remix/component";
994
+ import type { Handle, RemixNode } from "remix/component";
989
995
 
990
996
  export function MotionSafePanel(handle: Handle<{ children: RemixNode }>) {
991
997
  return () => <section mix={panelStyle}>{handle.props.children}</section>;