Repository: radix-ui/primitives Branch: main Commit: 22473d16404b Files: 649 Total size: 1.8 MB Directory structure: gitextract_7xaf4qlh/ ├── .changeset/ │ ├── README.md │ ├── changelog.cjs │ ├── config.json │ ├── silent-turkeys-fly.md │ └── ten-pumas-agree.md ├── .editorconfig ├── .github/ │ ├── CODEOWNERS │ ├── CONTRIBUTING.md │ ├── ISSUE_TEMPLATE/ │ │ ├── Bug_report.md │ │ ├── Documentation.md │ │ ├── Feature_request.md │ │ └── Question.md │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows/ │ ├── build.yml │ ├── chromatic.yml │ ├── publish-snapshot.yml │ └── publish-stable.yml ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── apps/ │ ├── ssr-testing/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── app/ │ │ │ ├── accessible-icon/ │ │ │ │ └── page.tsx │ │ │ ├── accordion/ │ │ │ │ └── page.tsx │ │ │ ├── alert-dialog/ │ │ │ │ └── page.tsx │ │ │ ├── avatar/ │ │ │ │ └── page.tsx │ │ │ ├── checkbox/ │ │ │ │ └── page.tsx │ │ │ ├── collapsible/ │ │ │ │ └── page.tsx │ │ │ ├── context-menu/ │ │ │ │ └── page.tsx │ │ │ ├── dialog/ │ │ │ │ └── page.tsx │ │ │ ├── dropdown-menu/ │ │ │ │ └── page.tsx │ │ │ ├── form/ │ │ │ │ └── page.tsx │ │ │ ├── hover-card/ │ │ │ │ └── page.tsx │ │ │ ├── label/ │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ ├── menubar/ │ │ │ │ └── page.tsx │ │ │ ├── navigation-menu/ │ │ │ │ └── page.tsx │ │ │ ├── one-time-password-field/ │ │ │ │ └── page.tsx │ │ │ ├── page.tsx │ │ │ ├── password-toggle-field/ │ │ │ │ └── page.tsx │ │ │ ├── popover/ │ │ │ │ └── page.tsx │ │ │ ├── portal/ │ │ │ │ ├── conditional-portal.tsx │ │ │ │ ├── custom-portal-container.tsx │ │ │ │ └── page.tsx │ │ │ ├── progress/ │ │ │ │ └── page.tsx │ │ │ ├── radio-group/ │ │ │ │ └── page.tsx │ │ │ ├── roving-focus-group/ │ │ │ │ ├── page.tsx │ │ │ │ └── roving-focus.client.tsx │ │ │ ├── scroll-area/ │ │ │ │ └── page.tsx │ │ │ ├── select/ │ │ │ │ └── page.tsx │ │ │ ├── separator/ │ │ │ │ └── page.tsx │ │ │ ├── slider/ │ │ │ │ └── page.tsx │ │ │ ├── slot/ │ │ │ │ └── page.tsx │ │ │ ├── switch/ │ │ │ │ └── page.tsx │ │ │ ├── tabs/ │ │ │ │ └── page.tsx │ │ │ ├── toast/ │ │ │ │ └── page.tsx │ │ │ ├── toggle-group/ │ │ │ │ └── page.tsx │ │ │ ├── toolbar/ │ │ │ │ └── page.tsx │ │ │ ├── tooltip/ │ │ │ │ └── page.tsx │ │ │ └── visually-hidden/ │ │ │ └── page.tsx │ │ ├── next-env.d.ts │ │ ├── next.config.js │ │ ├── package.json │ │ └── tsconfig.json │ └── storybook/ │ ├── .gitignore │ ├── .storybook/ │ │ ├── main.ts │ │ ├── manager-head.html │ │ ├── manager.ts │ │ ├── preview.css │ │ └── preview.ts │ ├── eslint.config.js │ ├── index.d.ts │ ├── package.json │ ├── stories/ │ │ ├── accessible-icon.stories.tsx │ │ ├── accordion.stories.module.css │ │ ├── accordion.stories.tsx │ │ ├── alert-dialog.stories.module.css │ │ ├── alert-dialog.stories.tsx │ │ ├── arrow.stories.tsx │ │ ├── aspect-ratio.stories.module.css │ │ ├── aspect-ratio.stories.tsx │ │ ├── avatar.stories.module.css │ │ ├── avatar.stories.tsx │ │ ├── checkbox.stories.module.css │ │ ├── checkbox.stories.tsx │ │ ├── collapsible.stories.module.css │ │ ├── collapsible.stories.tsx │ │ ├── collection.stories.tsx │ │ ├── context-menu.stories.module.css │ │ ├── context-menu.stories.tsx │ │ ├── dialog.stories.module.css │ │ ├── dialog.stories.tsx │ │ ├── dismissable-layer.stories.tsx │ │ ├── dropdown-menu.stories.module.css │ │ ├── dropdown-menu.stories.tsx │ │ ├── focus-scope.stories.tsx │ │ ├── form.stories.module.css │ │ ├── form.stories.tsx │ │ ├── hover-card.stories.module.css │ │ ├── hover-card.stories.tsx │ │ ├── label.stories.module.css │ │ ├── label.stories.tsx │ │ ├── menu.stories.module.css │ │ ├── menu.stories.tsx │ │ ├── menubar.stories.module.css │ │ ├── menubar.stories.tsx │ │ ├── navigation-menu.stories.module.css │ │ ├── navigation-menu.stories.tsx │ │ ├── one-time-password-field.stories.module.css │ │ ├── one-time-password-field.stories.tsx │ │ ├── password-toggle-field.stories.module.css │ │ ├── password-toggle-field.stories.tsx │ │ ├── popover.stories.module.css │ │ ├── popover.stories.tsx │ │ ├── popper.stories.module.css │ │ ├── popper.stories.tsx │ │ ├── portal.stories.tsx │ │ ├── presence.stories.module.css │ │ ├── presence.stories.tsx │ │ ├── progress.stories.module.css │ │ ├── progress.stories.tsx │ │ ├── radio-group.stories.module.css │ │ ├── radio-group.stories.tsx │ │ ├── roving-focus-group.stories.tsx │ │ ├── scroll-area.stories.module.css │ │ ├── scroll-area.stories.tsx │ │ ├── select.stories.module.css │ │ ├── select.stories.tsx │ │ ├── separator.stories.module.css │ │ ├── separator.stories.tsx │ │ ├── slider.stories.module.css │ │ ├── slider.stories.tsx │ │ ├── slot.stories.tsx │ │ ├── switch.stories.module.css │ │ ├── switch.stories.tsx │ │ ├── tabs.stories.module.css │ │ ├── tabs.stories.tsx │ │ ├── toast.stories.module.css │ │ ├── toast.stories.tsx │ │ ├── toggle-group.stories.module.css │ │ ├── toggle-group.stories.tsx │ │ ├── toggle.stories.module.css │ │ ├── toggle.stories.tsx │ │ ├── toolbar.stories.module.css │ │ ├── toolbar.stories.tsx │ │ ├── tooltip.stories.module.css │ │ ├── tooltip.stories.tsx │ │ └── visually-hidden.stories.tsx │ └── tsconfig.json ├── context7.json ├── cypress/ │ ├── e2e/ │ │ ├── ContextMenu.cy.ts │ │ ├── Dialog.cy.ts │ │ ├── DropdownMenu.cy.ts │ │ ├── Form.cy.ts │ │ ├── Menubar.cy.ts │ │ ├── Select.cy.ts │ │ └── Toast.cy.ts │ ├── support/ │ │ ├── commands.js │ │ ├── e2e.js │ │ └── index.d.ts │ └── tsconfig.json ├── cypress.config.ts ├── eslint.config.mjs ├── internal/ │ ├── builder/ │ │ ├── builder.js │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── radix-build.js │ │ └── tsconfig.json │ ├── eslint-config/ │ │ ├── eslint.config.js │ │ ├── index.js │ │ ├── package.json │ │ ├── react-package.js │ │ ├── tsconfig.json │ │ └── vite.js │ ├── test-data/ │ │ ├── eslint.config.js │ │ ├── foods.ts │ │ ├── package.json │ │ └── tsconfig.json │ └── typescript-config/ │ ├── base.json │ ├── nextjs.json │ ├── package.json │ ├── react-library/ │ │ └── index.d.ts │ ├── react-library.json │ ├── vite-app.json │ └── vite-node.json ├── package.json ├── packages/ │ ├── core/ │ │ ├── number/ │ │ │ ├── README.md │ │ │ ├── eslint.config.mjs │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── index.ts │ │ │ │ └── number.ts │ │ │ └── tsconfig.json │ │ ├── primitive/ │ │ │ ├── CHANGELOG.md │ │ │ ├── README.md │ │ │ ├── eslint.config.mjs │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── index.ts │ │ │ │ ├── primitive.tsx │ │ │ │ └── types.ts │ │ │ └── tsconfig.json │ │ └── rect/ │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── observe-element-rect.ts │ │ └── tsconfig.json │ └── react/ │ ├── accessible-icon/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── accesible-icon.test.tsx │ │ │ ├── accessible-icon.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── accordion/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── accordion.test.tsx │ │ │ ├── accordion.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── alert-dialog/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── alert-dialog.test.tsx │ │ │ ├── alert-dialog.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── announce/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── announce.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── arrow/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── arrow.test.tsx │ │ │ ├── arrow.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── aspect-ratio/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── aspect-ratio.test.tsx │ │ │ ├── aspect-ratio.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── avatar/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── avatar.test.tsx │ │ │ ├── avatar.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── checkbox/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── checkbox.test.tsx │ │ │ ├── checkbox.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── collapsible/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── collapsible.test.tsx │ │ │ ├── collapsible.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── collection/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── collection-legacy.tsx │ │ │ ├── collection.tsx │ │ │ ├── index.ts │ │ │ ├── ordered-dictionary.test.ts │ │ │ └── ordered-dictionary.ts │ │ └── tsconfig.json │ ├── compose-refs/ │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── compose-refs.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── context/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── create-context.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── context-menu/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── context-menu.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── dialog/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── dialog.test.tsx │ │ │ ├── dialog.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── direction/ │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── direction.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── dismissable-layer/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── dismissable-layer.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── dropdown-menu/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── dropdown-menu.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── focus-guards/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── focus-guards.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── focus-scope/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── focus-scope.test.tsx │ │ │ ├── focus-scope.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── form/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── form.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── hover-card/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── hover-card.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── id/ │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── id.tsx │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── label/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── label.tsx │ │ └── tsconfig.json │ ├── menu/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── menu.tsx │ │ └── tsconfig.json │ ├── menubar/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── menubar.tsx │ │ └── tsconfig.json │ ├── navigation-menu/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── navigation-menu.tsx │ │ └── tsconfig.json │ ├── one-time-password-field/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── one-time-password-field.test.tsx │ │ │ └── one-time-password-field.tsx │ │ └── tsconfig.json │ ├── password-toggle-field/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── password-toggle-field.test.tsx │ │ │ └── password-toggle-field.tsx │ │ └── tsconfig.json │ ├── popover/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── popover.tsx │ │ └── tsconfig.json │ ├── popper/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── popper.tsx │ │ └── tsconfig.json │ ├── portal/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── portal.tsx │ │ └── tsconfig.json │ ├── presence/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── presence.tsx │ │ │ └── use-state-machine.tsx │ │ └── tsconfig.json │ ├── primitive/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── primitive.tsx │ │ └── tsconfig.json │ ├── progress/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── progress.tsx │ │ └── tsconfig.json │ ├── radio-group/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── radio-group.tsx │ │ │ └── radio.tsx │ │ └── tsconfig.json │ ├── radix-ui/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── internal.ts │ │ └── tsconfig.json │ ├── roving-focus/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── roving-focus-group.tsx │ │ └── tsconfig.json │ ├── scroll-area/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── scroll-area.tsx │ │ │ └── use-state-machine.ts │ │ └── tsconfig.json │ ├── select/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── select.tsx │ │ └── tsconfig.json │ ├── separator/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── separator.tsx │ │ └── tsconfig.json │ ├── slider/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── slider.tsx │ │ └── tsconfig.json │ ├── slot/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── __snapshots__/ │ │ │ │ └── slot.test.tsx.snap │ │ │ ├── index.ts │ │ │ ├── slot.test.tsx │ │ │ └── slot.tsx │ │ └── tsconfig.json │ ├── switch/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── switch.test.tsx │ │ │ └── switch.tsx │ │ └── tsconfig.json │ ├── tabs/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── tabs.tsx │ │ └── tsconfig.json │ ├── toast/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── toast.tsx │ │ └── tsconfig.json │ ├── toggle/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── toggle.test.tsx │ │ │ └── toggle.tsx │ │ └── tsconfig.json │ ├── toggle-group/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── toggle-group.test.tsx │ │ │ └── toggle-group.tsx │ │ └── tsconfig.json │ ├── toolbar/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── toolbar.test.tsx │ │ │ └── toolbar.tsx │ │ └── tsconfig.json │ ├── tooltip/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── tooltip.test.tsx │ │ │ └── tooltip.tsx │ │ └── tsconfig.json │ ├── use-callback-ref/ │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── use-callback-ref.tsx │ │ └── tsconfig.json │ ├── use-controllable-state/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── use-controllable-state-reducer.tsx │ │ │ ├── use-controllable-state.test.tsx │ │ │ └── use-controllable-state.tsx │ │ └── tsconfig.json │ ├── use-effect-event/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── use-effect-event.tsx │ │ └── tsconfig.json │ ├── use-escape-keydown/ │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── use-escape-keydown.tsx │ │ └── tsconfig.json │ ├── use-is-hydrated/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── use-is-hydrated.tsx │ │ └── tsconfig.json │ ├── use-layout-effect/ │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── use-layout-effect.tsx │ │ └── tsconfig.json │ ├── use-previous/ │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── use-previous.tsx │ │ └── tsconfig.json │ ├── use-rect/ │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── use-rect.tsx │ │ └── tsconfig.json │ ├── use-size/ │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── use-size.tsx │ │ └── tsconfig.json │ └── visually-hidden/ │ ├── CHANGELOG.md │ ├── README.md │ ├── eslint.config.mjs │ ├── package.json │ ├── src/ │ │ ├── index.ts │ │ └── visually-hidden.tsx │ └── tsconfig.json ├── patches/ │ └── @changesets__apply-release-plan.patch ├── philosophy.md ├── pnpm-workspace.yaml ├── release-process.md ├── scripts/ │ └── setup-tests.ts ├── types/ │ ├── global.d.ts │ └── index.d.ts └── vitest.config.mts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .changeset/README.md ================================================ # Changesets Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works with multi-package repos, or single-package repos to help you version and publish your code. You can find the full documentation for it [in our repository](https://github.com/changesets/changesets) We have a quick list of common questions to get you started engaging with this project in [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) ================================================ FILE: .changeset/changelog.cjs ================================================ // https://github.com/ariakit/ariakit/blob/main/.changeset/changelog.cjs // MIT License, Copyright (c) Diego Haz /** @type {import("@changesets/types").ChangelogFunctions["getDependencyReleaseLine"]} */ async function getDependencyReleaseLine(_, dependenciesUpdated) { if (dependenciesUpdated.length === 0) return ''; const updatedDepenenciesList = dependenciesUpdated.map( (dependency) => `\`${dependency.name}@${dependency.newVersion}\``, ); return `- Updated dependencies: ${updatedDepenenciesList.join(', ')}`; } /** @type {import("@changesets/types").ChangelogFunctions["getReleaseLine"]} */ async function getReleaseLine(changeset) { const [firstLine, ...nextLines] = changeset.summary.split('\n').map((l) => l.trimEnd()); if (!nextLines.length) return `- ${firstLine}`; return `### ${firstLine}\n${nextLines.join('\n')}`; } /** * @param {Array} changelogLines */ async function getChangelogText(changelogLines) { const lines = await Promise.all(changelogLines); if (!lines.length) return ''; const isOverviewLine = (l) => l.startsWith('### Overview\n'); const isHeadingLine = (l) => l.startsWith('###'); const isOtherLine = (l) => !l.startsWith('###'); const headingLines = lines .filter(isHeadingLine) .sort((a, b) => { if (isOverviewLine(a)) return -1; if (isOverviewLine(b)) return 1; return 0; }) .map((l) => l.replace('### Overview\n\n', '')); const otherLines = lines.filter(isOtherLine); if (!headingLines.length && !otherLines.length) return ''; const other = otherLines.join('\n'); if (!headingLines.length) return other; const heading = headingLines.join('\n\n'); if (!otherLines.length) return heading; return `${heading}\n\n### Other updates\n\n${other}`; } /** * @param {import("@changesets/types").ModCompWithPackage} release * @param {Record<"major" | "minor" | "patch", Array>} changelogLines */ async function getChangelogEntry(release, changelogLines) { // const date = new Date().toLocaleDateString("en-US", { // month: "long", // day: "numeric", // year: "numeric", // }); const text = await getChangelogText(Object.values(changelogLines).flat()); return `## ${release.newVersion}\n\n${text}`; } module.exports = { getDependencyReleaseLine, getReleaseLine, getChangelogEntry, }; ================================================ FILE: .changeset/config.json ================================================ { "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json", "changelog": "./changelog.cjs", "access": "public", "commit": false, "fixed": [], "linked": [], "baseBranch": "main", "updateInternalDependencies": "patch", "snapshot": { "useCalculatedVersion": true, "prereleaseTemplate": "rc.{timestamp}" }, "ignore": ["@repo/ssr-testing", "@repo/storybook"] } ================================================ FILE: .changeset/silent-turkeys-fly.md ================================================ --- '@radix-ui/react-collection': patch --- Updated `unstable_createCollection` signature to extend `BaseItemData` internally ================================================ FILE: .changeset/ten-pumas-agree.md ================================================ --- '@radix-ui/react-direction': patch --- Added `use client` directive to module entrypoint ================================================ FILE: .editorconfig ================================================ root = true [*] charset = utf-8 end_of_line = lf indent_size = 2 indent_style = space insert_final_newline = true max_line_length = 80 tab_width = 2 trim_trailing_whitespace = true [Makefile] indent_style = tab tab_width = 4 [{*.md,*.mdx}] trim_trailing_whitespace = false ================================================ FILE: .github/CODEOWNERS ================================================ # Add the following users as reviewers on new pull requests - @lucasmotta @hadihallak @chaance ================================================ FILE: .github/CONTRIBUTING.md ================================================ # Contributing to Radix Primitives ## Code of Conduct Radix has adopted the [Contributor Covenant](https://www.contributor-covenant.org/) as its Code of Conduct, and we expect project participants to adhere to it. Please read [the full text](/CODE_OF_CONDUCT.md) so that you can understand what actions will and will not be tolerated. ## Heuristics [heuristic]() /ˌhjʊ(ə)ˈrɪstɪk/ > A technique designed for solving a problem more quickly when classic methods are too slow, or for finding an approximate solution when classic methods fail to find any exact solution - Priority is the best User Experience - Complexity should be introduced when it’s inevitable - Code should be easy to reason about - Code should be easy to delete - Avoid abstracting too early - Avoid thinking too far in the future ## Questions If you have questions about Radix Primitives, be sure to check out the docs where we have several examples and detailed API references that may help you solve your problem. You can also share your questions on [GitHub Discussions](https://github.com/radix-ui/primitives/discussions). ## How to contribute There are many ways to contribute to the project. Code is just one possible means of contribution. - **Feedback.** Tell us what we're doing well or where we can improve. - **Support.** You can answer questions on StackOverflow or [GitHub Discussions](https://github.com/radix-ui/primitives/discussions), or provide solutions for others in [open issues](https://github.com/radix-ui/primitives/issues). - **Write.** If you come up with an interesting example, write about it. Post it to your blog and share it with us. We'd love to see what folks in the community build with Primitives! - **Report.** Create issues with bug reports so we can make Primitives even better. ## Working on your first Pull Request? There are a lot of great resources on creating a good pull request. We've included a few below, but don't be shy—we appreciate all contriibutions and are happy to help those who are willing to help us! - [How to Contribute to a Project on GitHub](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github) ## Preparing a Pull Request [Pull Requests](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request) are always welcome, but before working on a large change, it is best to open an issue first to discuss it with maintainers. A good PR is small, focuses on a single feature or improvement, and clearly communicates the problem it solves. Try not to include more than one issue in a single PR. It's much easier for us to review multiple small pull requests than one that is large and unwieldy. 1. [Fork the repository](https://docs.github.com/en/free-pro-team@latest/github/getting-started-with-github/fork-a-repo). 2. Clone the fork to your local machine and add upstream remote: ```sh git clone https://github.com//primitives.git cd primitives git remote add upstream https://github.com/radix-ui/primitives.git ``` 3. Synchronize your local `main` branch with the upstream remote: ```sh git checkout main git pull upstream main ``` 4. Make sure your Node version matches the [.nvmrc](../.nvmrc). ``` node -v ``` 1. Install dependencies with [pnpm](https://pnpm.io): ```sh pnpm install ``` 6. Create a new branch related to your PR: ```sh git checkout -b my-bug-fix ``` 7. Make changes, then commit and push to your forked repository: ```sh git push -u origin HEAD ``` 8. Go to [the repository](https://github.com/radix-ui/primitives) and [make a Pull Request](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request). 9. We will review your Pull Request and either merge it, request changes to it, or close it with an explanation. ## Working locally The repo is managed with pnpm workspaces. ### Development ```bash # install dependencies pnpm install # start Storybook and see examples in the browser pnpm dev ``` Make your changes and check that they resolve the problem with an example in Storybook. We also suggest adding tests to support your change, and then run `pnpm test` to make sure nothing is broken. You also need to inform Changesets that a particular package has changed for proper versioning. Run `pnpm changeset` to mark the appropriate type of change for those packages, then commit the resulting Changeset files. Lastly, run `pnpm build` to ensure that the build runs successfully before submitting the pull request. ================================================ FILE: .github/ISSUE_TEMPLATE/Bug_report.md ================================================ --- name: 'Bug report' about: 'Create a bug report' --- ## Bug report ### Current Behavior ### Expected behavior ### Reproducible example [CodeSandbox Template](https://codesandbox.io/s/2r30e) ### Suggested solution ### Additional context ### Your environment | Software | Name(s) | Version | | ---------------- | ------- | ------- | | Radix Package(s) | | | | React | n/a | | | Browser | | | | Assistive tech | | | | Node | n/a | | | npm/yarn/pnpm | | | | Operating System | | | ================================================ FILE: .github/ISSUE_TEMPLATE/Documentation.md ================================================ --- name: 'Documentation' about: 'Suggestions for Radix documentation' --- ## Documentation ### Relevant Radix Component(s) ### Examples from other doc sites ================================================ FILE: .github/ISSUE_TEMPLATE/Feature_request.md ================================================ --- name: 'Feature request' about: 'Suggest an idea for a feature or a new component' --- ## Feature request ### Overview ### Examples in other libraries ### Who does this impact? Who is this for? ### Additional context ================================================ FILE: .github/ISSUE_TEMPLATE/Question.md ================================================ --- name: 'Question' about: 'For usage questions, please use Stack Overflow or use GitHub Discussions' --- ## Question For usage questions, please use Stack Overflow or use [GitHub Discussions](https://github.com/radix-ui/primitives/discussions) instead of posting an issue. This helps us keep bugs and feature requests prioritized, and it increases the liklihood that more people see your question and can answer it more qiuckly! ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ ### Description ================================================ FILE: .github/workflows/build.yml ================================================ name: Build on: pull_request: branches: - main concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: test: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup pnpm uses: pnpm/action-setup@v4 with: run_install: false - name: Setup Node uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Lint run: pnpm lint - name: Run build run: pnpm build - name: Run tests run: pnpm test:ci ================================================ FILE: .github/workflows/chromatic.yml ================================================ name: 'Chromatic' on: push: branches: - main # "You must append a colon (:) to all events, including events without configuration." # https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#example-using-multiple-events-with-activity-types-or-configuration pull_request: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: chromatic-deployment: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup pnpm uses: pnpm/action-setup@v4 with: run_install: false - name: Setup Node uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Publish to Chromatic uses: chromaui/action@v1 with: token: ${{ secrets.GITHUB_TOKEN }} projectToken: 63f2bd83c33c exitOnceUploaded: true exitZeroOnChanges: true ================================================ FILE: .github/workflows/publish-snapshot.yml ================================================ name: 'Publish Snapshot Release' on: push: branches: - main paths-ignore: # - '.github/**' - './package.json' - './changeset/**/*' - '/apps/**/*' - '/internal/**/*' - '/cypress/**/*' - '**/*.test.ts' - '**/*.test.tsx' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} concurrency: ${{ github.workflow }}-${{ github.ref }} jobs: publish-snapshot: if: github.repository == 'radix-ui/primitives' runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup pnpm uses: pnpm/action-setup@v4 with: run_install: false - name: Setup Node uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' registry-url: 'https://registry.npmjs.org' cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Build packages run: pnpm build - name: Write .npmrc run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc - name: Update snapshot version run: pnpm run bump:next - name: Publish snapshot run: pnpm run release:next --no-git-checks ================================================ FILE: .github/workflows/publish-stable.yml ================================================ name: 'Publish Stable Release' on: push: branches: - stable env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} concurrency: ${{ github.workflow }}-${{ github.ref }} jobs: publish-stable: if: github.repository == 'radix-ui/primitives' name: Version or publish timeout-minutes: 15 runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup pnpm uses: pnpm/action-setup@v4 with: run_install: false - name: Setup Node uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' registry-url: 'https://registry.npmjs.org' cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Build packages run: pnpm build - name: Write .npmrc run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc - name: Create Release Pull Request or Publish to npm id: changesets uses: changesets/action@v1 with: title: New release commit: New release version: pnpm run bump:stable publish: pnpm run release:stable:ci createGithubReleases: false ================================================ FILE: .gitignore ================================================ *.log* .DS_Store node_modules dist test-output.* .cache storybook-static .env .eslintcache .pnp .pnp.js *.pem .npmrc # Testing cypress/videos cypress/screenshots # Editor-specific .vscode .idea # So devs can maintain their own todo lists in the project TODO.md ================================================ FILE: .nvmrc ================================================ 22 ================================================ FILE: .prettierignore ================================================ .next node_modules .yarn dist storybook-static .pnp .pnp.js coverage pnpm-lock.yaml package-lock.json yarn.lock *.log* .DS_Store *.pem ================================================ FILE: .prettierrc ================================================ { "printWidth": 100, "singleQuote": true } ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: - Using welcoming and inclusive language - Being respectful of differing viewpoints and experiences - Gracefully accepting constructive criticism - Focusing on what is best for the community - Showing empathy towards other community members Examples of unacceptable behavior by participants include: - The use of sexualized language or imagery and unwelcome sexual attention or advances - Trolling, insulting/derogatory comments, and personal or political attacks - Public or private harassment - Publishing others' private information, such as a physical or electronic address, without explicit permission - Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at colm@workos.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2022 WorkOS Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ [![Radix Primitives Logo](primitives.png)](https://radix-ui.com/primitives) # Radix Primitives **An open-source UI component library for building high-quality, accessible design systems and web apps.** Radix Primitives is a low-level UI component library with a focus on accessibility, customization and developer experience. You can use these components either as the base layer of your design system, or adopt them incrementally. --- ## Installation First, install pnpm if you haven't already. Open your terminal and run: ```bash npm install -g pnpm ``` Then, install the dependencies: ```bash pnpm install ``` ## Documentation For full documentation, visit [radix-ui.com/primitives/docs](https://www.radix-ui.com/primitives/docs). ## Releases For changelog, visit [radix-ui.com/primitives/docs/overview/releases](https://www.radix-ui.com/primitives/docs/overview/releases). ## Contributing Please follow our [contributing guidelines](./.github/CONTRIBUTING.md). --- ## Community - [Discord](https://discord.com/invite/7Xb99uG) - To get involved with the Radix community, ask questions and share tips. - [Twitter](https://twitter.com/radix_ui) - To receive updates, announcements, blog posts, and general Radix tips. ## Thanks Chromatic Thanks to [Chromatic](https://www.chromatic.com/) for providing the visual testing platform that helps us review UI changes and catch visual regressions. --- ## License Licensed under the MIT License, Copyright © 2022-present [WorkOS](https://workos.com). ================================================ FILE: apps/ssr-testing/.gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp .pnp.js # testing /coverage # next.js /.next/ /out/ # production /build # misc .DS_Store *.pem # debug *.log* # local env files .env.local .env.development.local .env.test.local .env.production.local # vercel .vercel ================================================ FILE: apps/ssr-testing/README.md ================================================ # `@repo/ssr-testing` This is a testing playground for SSR support in our primitives using a [Next.js](https://nextjs.org/) project. ## Getting Started From the root of the repo, run the development server: ```sh pnpm dev:ssr ``` ================================================ FILE: apps/ssr-testing/app/accessible-icon/page.tsx ================================================ import * as React from 'react'; import { AccessibleIcon } from 'radix-ui'; export default function Page() { return ( ); } ================================================ FILE: apps/ssr-testing/app/accordion/page.tsx ================================================ import * as React from 'react'; import { Accordion } from 'radix-ui'; export default function Page() { return ( One Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. Two Cursus sed mattis commodo fermentum conubia ipsum pulvinar sagittis, diam eget bibendum porta nascetur ac dictum, leo tellus dis integer platea ultrices mi. Three (disabled) Sociis hac sapien turpis conubia sagittis justo dui, inceptos penatibus feugiat himenaeos euismod magna, nec tempor pulvinar eu etiam mattis. Four Odio placerat quisque sapien sagittis non sociis ligula penatibus dignissim vitae, enim vulputate nullam semper potenti etiam volutpat libero. ); } ================================================ FILE: apps/ssr-testing/app/alert-dialog/page.tsx ================================================ import * as React from 'react'; import { AlertDialog } from 'radix-ui'; export default function Page() { return ( delete everything Are you sure? This will do a very dangerous thing. Thar be dragons! yolo, do it maybe not ); } ================================================ FILE: apps/ssr-testing/app/avatar/page.tsx ================================================ import * as React from 'react'; import { Avatar } from 'radix-ui'; export default function Page() { return ( A ); } ================================================ FILE: apps/ssr-testing/app/checkbox/page.tsx ================================================ import * as React from 'react'; import { Checkbox } from 'radix-ui'; export default function Page() { return ( [ ] ); } ================================================ FILE: apps/ssr-testing/app/collapsible/page.tsx ================================================ import * as React from 'react'; import { Collapsible } from 'radix-ui'; export default function Page() { return ( Trigger Content ); } ================================================ FILE: apps/ssr-testing/app/context-menu/page.tsx ================================================ import * as React from 'react'; import { ContextMenu } from 'radix-ui'; export default function Page() { return ( Right click here Undo Redo Cut Copy Paste ); } ================================================ FILE: apps/ssr-testing/app/dialog/page.tsx ================================================ import * as React from 'react'; import { Dialog } from 'radix-ui'; export default function Page() { return ( open Title Description close ); } ================================================ FILE: apps/ssr-testing/app/dropdown-menu/page.tsx ================================================ import * as React from 'react'; import { DropdownMenu } from 'radix-ui'; export default function Page() { return ( Open Undo Redo Cut Copy Paste ); } ================================================ FILE: apps/ssr-testing/app/form/page.tsx ================================================ import * as React from 'react'; import { Form } from 'radix-ui'; export default function Page() { return ( Email Value is missing Email is invalid ); } ================================================ FILE: apps/ssr-testing/app/hover-card/page.tsx ================================================ import * as React from 'react'; import { HoverCard } from 'radix-ui'; export default function Page() { return ( Hover me Nicely done! ); } ================================================ FILE: apps/ssr-testing/app/label/page.tsx ================================================ import * as React from 'react'; import { Label } from 'radix-ui'; export default function Page() { return Label; } ================================================ FILE: apps/ssr-testing/app/layout.tsx ================================================ import * as React from 'react'; import type { Metadata } from 'next'; import Link from 'next/link'; export default function Layout({ children }: { children: React.ReactNode }) { return (

SSR / RSC testing

AccessibleIcon Accordion AlertDialog Avatar Checkbox Collapsible ContextMenu Dialog DropdownMenu Form HoverCard Label Menubar NavigationMenu OneTimePasswordField PasswordToggleField Popover Portal Progress RadioGroup RovingFocusGroup ScrollArea Select Separator Slider Slot Switch Tabs Toast ToggleGroup Toolbar Tooltip VisuallyHidden
{children}
); } export const metadata: Metadata = { title: 'SSR testing', }; ================================================ FILE: apps/ssr-testing/app/menubar/page.tsx ================================================ import * as React from 'react'; import { Menubar } from 'radix-ui'; export default function Page() { return ( Open Menu Item 1 Item 2 Item 3 ); } ================================================ FILE: apps/ssr-testing/app/navigation-menu/page.tsx ================================================ import * as React from 'react'; import { NavigationMenu } from 'radix-ui'; export default function Page() { return ( Nav Menu Item 1 Link Nav Menu Item 2 ); } ================================================ FILE: apps/ssr-testing/app/one-time-password-field/page.tsx ================================================ import * as React from 'react'; import { unstable_OneTimePasswordField as OneTimePasswordField } from 'radix-ui'; export default function Page() { return (

With indices

); } ================================================ FILE: apps/ssr-testing/app/page.tsx ================================================ export default function Page() { return null; } ================================================ FILE: apps/ssr-testing/app/password-toggle-field/page.tsx ================================================ import * as React from 'react'; import { unstable_PasswordToggleField as PasswordToggleField } from 'radix-ui'; export default function Page() { return (
} hidden={} />
); } function EyeClosedIcon() { return ( ); } function EyeOpenIcon() { return ( ); } ================================================ FILE: apps/ssr-testing/app/popover/page.tsx ================================================ import * as React from 'react'; import { Popover } from 'radix-ui'; export default function Page() { return ( open close ); } ================================================ FILE: apps/ssr-testing/app/portal/conditional-portal.tsx ================================================ 'use client'; import * as React from 'react'; import { Portal } from 'radix-ui'; export const ConditionalPortal = () => { const [container, setContainer] = React.useState(null); const [open, setOpen] = React.useState(false); return (
{open && ( This content is rendered in a custom container )}
); }; ================================================ FILE: apps/ssr-testing/app/portal/custom-portal-container.tsx ================================================ 'use client'; import * as React from 'react'; import { Portal } from 'radix-ui'; export const CustomPortalContainer = () => { const [container, setContainer] = React.useState(null); return (
This content is rendered in a custom container
); }; ================================================ FILE: apps/ssr-testing/app/portal/page.tsx ================================================ import * as React from 'react'; import { Portal } from 'radix-ui'; import { CustomPortalContainer } from './custom-portal-container'; import { ConditionalPortal } from './conditional-portal'; export default function Page() { return (

This content is rendered in the main DOM tree

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quos porro, est ex quia itaque facere fugit necessitatibus aut enim. Nisi rerum quae, repellat in perspiciatis explicabo laboriosam necessitatibus eius pariatur.

This content is rendered in a portal (another DOM tree)

Because of the portal, it can appear in a different DOM tree from the main one (by default a new element inside the body), even though it is part of the same React tree.



); } ================================================ FILE: apps/ssr-testing/app/progress/page.tsx ================================================ import * as React from 'react'; import { Progress } from 'radix-ui'; export default function Page() { return ( Progress ); } ================================================ FILE: apps/ssr-testing/app/radio-group/page.tsx ================================================ import * as React from 'react'; import { Label, RadioGroup } from 'radix-ui'; export default function Page() { return ( Favourite pet [ X ] Cat
[ X ] Dog
[ X ] Rabbit
); } ================================================ FILE: apps/ssr-testing/app/roving-focus-group/page.tsx ================================================ import * as React from 'react'; import { RovingFocusProvider, RovingFocusToggle, ButtonGroup, Button } from './roving-focus.client'; export default function Page() { return ( <>

Basic

no orientation (both) + no looping

no orientation (both) + looping

horizontal orientation + no looping

horizontal orientation + looping

vertical orientation + no looping

vertical orientation + looping

Nested

); } ================================================ FILE: apps/ssr-testing/app/roving-focus-group/roving-focus.client.tsx ================================================ 'use client'; import * as React from 'react'; import { composeEventHandlers, RovingFocus } from 'radix-ui/internal'; type Direction = 'ltr' | 'rtl'; const RovingFocusContext = React.createContext<{ dir: 'ltr' | 'rtl'; setDir: React.Dispatch>; }>({ dir: 'ltr', setDir: () => void 0, }); RovingFocusContext.displayName = 'RovingFocusContext'; export function RovingFocusProvider({ children }: { children: React.ReactNode }) { const [dir, setDir] = React.useState('ltr'); return (
{children}
); } export function RovingFocusToggle() { const { dir, setDir } = React.use(RovingFocusContext); return ( ); } const ButtonGroupContext = React.createContext<{ value?: string; setValue: React.Dispatch>; }>({} as any); type ButtonGroupProps = Omit, 'defaultValue'> & RovingFocus.RovingFocusGroupProps & { defaultValue?: string }; export function ButtonGroup({ defaultValue, ...props }: ButtonGroupProps) { const [value, setValue] = React.useState(defaultValue); const { dir } = React.use(RovingFocusContext); return ( ); } type ButtonProps = Omit, 'value'> & { value?: string }; export function Button(props: ButtonProps) { const { value: contextValue, setValue } = React.use(ButtonGroupContext); const isSelected = contextValue !== undefined && props.value !== undefined && contextValue === props.value; return ( ); export const Chromatic = () => (

Some text with an inline accessible icon{' '}

); Chromatic.parameters = { chromatic: { disable: false } }; const CrossIcon = () => ( ); ================================================ FILE: apps/storybook/stories/accordion.stories.module.css ================================================ .root { font-family: sans-serif; &[data-orientation='horizontal'] { display: flex; max-width: 40em; height: 50vh; } &[data-orientation='vertical'] { max-width: 20em; } } .item { &[data-orientation='horizontal'] { display: flex; border-right: 1px solid var(--gray-1); } &[data-orientation='vertical'] { border-bottom: 1px solid var(--gray-1); } } .header { margin: 0; &[data-orientation='horizontal'] { height: 100%; } } .trigger { /* because it's a button, we want to stretch it */ &[data-orientation='horizontal'] { height: 100%; } &[data-orientation='vertical'] { width: 100%; } text-align: inherit; box-sizing: border-box; appearance: none; border: none; padding: 10px; background-color: black; color: var(--gray-1); font-family: inherit; font-size: 1.2em; &:focus { outline: 2px solid var(--red-8); color: var(--red-9); } &[data-disabled] { color: var(--gray-9); } &[data-state='open'] { background-color: var(--red-9); color: var(--gray-1); &:focus { color: var(--gray-12); } } } .content { padding: 10px; line-height: 1.5; } .animatedContent { overflow: hidden; &[data-state='open'] { animation: accordion-slideDown 300ms ease-out; } &[data-state='closed'] { animation: accordion-slideUp 300ms ease-out; } } .animated2DContent { overflow: hidden; &[data-state='open'] { animation: accordion-open2D 1000ms ease-out; } &[data-state='closed'] { animation: accordion-close2D 1000ms ease-out; } } .rootAttr, .itemAttr, .headerAttr, .triggerAttr, .contentAttr { background-color: var(--blue-a12); border: 2px solid var(--blue-9); padding: 10px; &[data-state='closed'] { border-color: var(--red-9); } &[data-state='open'] { border-color: var(--green-9); } &[data-disabled] { border-style: dashed; } &:disabled { opacity: 0.5; } } .contentAttr { /* ensure we can see the content (because it has `hidden` attribute) */ display: block; } @keyframes accordion-slideDown { from { height: 0; } to { height: var(--radix-accordion-content-height); } } @keyframes accordion-slideUp { from { height: var(--radix-accordion-content-height); } to { height: 0; } } @keyframes accordion-open2D { from { width: 0; height: 0; } to { width: var(--radix-accordion-content-width); height: var(--radix-accordion-content-height); } } @keyframes accordion-close2D { from { width: var(--radix-accordion-content-width); height: var(--radix-accordion-content-height); } to { width: 0; height: 0; } } ================================================ FILE: apps/storybook/stories/accordion.stories.tsx ================================================ /* eslint-disable jsx-a11y/anchor-is-valid */ import * as React from 'react'; import { Accordion } from 'radix-ui'; import styles from './accordion.stories.module.css'; export default { title: 'Components/Accordion' }; export const Single = () => { const [valueOne, setValueOne] = React.useState('one'); return ( <>

Uncontrolled

One Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. Two Cursus sed mattis commodo fermentum conubia ipsum pulvinar sagittis, diam eget bibendum porta nascetur ac dictum, leo tellus dis integer platea ultrices mi. Three (disabled) Sociis hac sapien turpis conubia sagittis justo dui, inceptos penatibus feugiat himenaeos euismod magna, nec tempor pulvinar eu etiam mattis. Four Odio placerat quisque sapien sagittis non sociis ligula penatibus dignissim vitae, enim vulputate nullam semper potenti etiam volutpat libero.

Controlled

One Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. Two Cursus sed mattis commodo fermentum conubia ipsum pulvinar sagittis, diam eget bibendum porta nascetur ac dictum, leo tellus dis integer platea ultrices mi. Three (disabled) Sociis hac sapien turpis conubia sagittis justo dui, inceptos penatibus feugiat himenaeos euismod magna, nec tempor pulvinar eu etiam mattis. Four Odio placerat quisque sapien sagittis non sociis ligula penatibus dignissim vitae, enim vulputate nullam semper potenti etiam volutpat libero.

Collapsible

One Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. Two Cursus sed mattis commodo fermentum conubia ipsum pulvinar sagittis, diam eget bibendum porta nascetur ac dictum, leo tellus dis integer platea ultrices mi. Three (disabled) Sociis hac sapien turpis conubia sagittis justo dui, inceptos penatibus feugiat himenaeos euismod magna, nec tempor pulvinar eu etiam mattis. Four Odio placerat quisque sapien sagittis non sociis ligula penatibus dignissim vitae, enim vulputate nullam semper potenti etiam volutpat libero. ); }; export const Multiple = () => { const [value, setValue] = React.useState(['one', 'two']); return ( <>

Uncontrolled

One Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. Two Cursus sed mattis commodo fermentum conubia ipsum pulvinar sagittis, diam eget bibendum porta nascetur ac dictum, leo tellus dis integer platea ultrices mi. Three (disabled) Sociis hac sapien turpis conubia sagittis justo dui, inceptos penatibus feugiat himenaeos euismod magna, nec tempor pulvinar eu etiam mattis. Four Odio placerat quisque sapien sagittis non sociis ligula penatibus dignissim vitae, enim vulputate nullam semper potenti etiam volutpat libero.

Controlled

One Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. Two Cursus sed mattis commodo fermentum conubia ipsum pulvinar sagittis, diam eget bibendum porta nascetur ac dictum, leo tellus dis integer platea ultrices mi. Three (disabled) Sociis hac sapien turpis conubia sagittis justo dui, inceptos penatibus feugiat himenaeos euismod magna, nec tempor pulvinar eu etiam mattis. Four Odio placerat quisque sapien sagittis non sociis ligula penatibus dignissim vitae, enim vulputate nullam semper potenti etiam volutpat libero. ); }; export const Animated = () => { const values = ['One', 'Two', 'Three', 'Four']; const [count, setCount] = React.useState(1); const [hasDynamicContent, setHasDynamicContent] = React.useState(false); const timerRef = React.useRef(0); React.useEffect(() => { if (hasDynamicContent) { timerRef.current = window.setTimeout(() => { setCount((prevCount) => { const nextCount = prevCount < 5 ? prevCount + 1 : prevCount; if (nextCount === 5) setHasDynamicContent(false); return nextCount; }); }, 3000); return () => { clearTimeout(timerRef.current); }; } }, [count, hasDynamicContent]); return ( <>

Closed by default

{values.map((value) => ( {value} {[...Array(count)].map((_, index) => (
Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed.
))}
))}

Open by default

{values.map((value) => ( {value} {[...Array(count)].map((_, index) => (
Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed.
))}
))}
); }; export const Animated2D = () => { const values = ['One', 'Two', 'Three', 'Four']; return ( <> {values.map((value) => ( {value}
Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed.
))}
); }; export const AnimatedControlled = () => { const [value, setValue] = React.useState(['one', 'two', 'three', 'four']); return ( One Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. Two Cursus sed mattis commodo fermentum conubia ipsum pulvinar sagittis, diam eget bibendum porta nascetur ac dictum, leo tellus dis integer platea ultrices mi. Three Sociis hac sapien turpis conubia sagittis justo dui, inceptos penatibus feugiat himenaeos euismod magna, nec tempor pulvinar eu etiam mattis. Four Odio placerat quisque sapien sagittis non sociis ligula penatibus dignissim vitae, enim vulputate nullam semper potenti etiam volutpat libero. ); }; export const OutsideViewport = () => ( <>

Scroll down to see tabs

When accordion buttons are focused and the user is navigating via keyboard, the page should not scroll unless the next tab is entering the viewport.

One Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. Two Cursus sed mattis commodo fermentum conubia ipsum pulvinar sagittis, diam eget bibendum porta nascetur ac dictum, leo tellus dis integer platea ultrices mi. Three (disabled) Sociis hac sapien turpis conubia sagittis justo dui, inceptos penatibus feugiat himenaeos euismod magna, nec tempor pulvinar eu etiam mattis. Four Odio placerat quisque sapien sagittis non sociis ligula penatibus dignissim vitae, enim vulputate nullam semper potenti etiam volutpat libero.
); export const Horizontal = () => ( <>

Horizontal Orientation

One Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. Two Cursus sed mattis commodo fermentum conubia ipsum pulvinar sagittis, diam eget bibendum porta nascetur ac dictum, leo tellus dis integer platea ultrices mi. Three (disabled) Sociis hac sapien turpis conubia sagittis justo dui, inceptos penatibus feugiat himenaeos euismod magna, nec tempor pulvinar eu etiam mattis. Four Odio placerat quisque sapien sagittis non sociis ligula penatibus dignissim vitae, enim vulputate nullam semper potenti etiam volutpat libero. ); export const Chromatic = () => { const items = ['One', 'Two', 'Three', 'Four']; return ( <>

Uncontrolled

Single closed

{items.map((item) => ( {item} {item}: Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. ))}

Single open

{items.map((item) => ( {item} {item}: Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. ))}

Multiple closed

{items.map((item) => ( {item} {item}: Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. ))}

Multiple open

{items.map((item) => ( {item} {item}: Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. ))}

Controlled

Single open

{items.map((item) => ( {item} {item}: Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. ))}

Multiple open

{items.map((item) => ( {item} {item}: Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. ))}

Disabled (whole)

{items.map((item) => ( {item} {item}: Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. ))}

Disabled (item)

Just item

{items.map((item) => ( {item} {item}: Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. ))}

with `disabled=false` on top-level

{items.map((item) => ( {item} {item}: Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. ))}

Force mounted contents

{items.map((item) => ( {item} {item}: Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. ))}

State attributes

Accordion disabled

{items.map((item) => ( {item} {item}: Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. ))}

Accordion enabled with item override

{items.map((item) => ( {item} {item}: Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. ))}

Accordion disabled with item override

{items.map((item) => ( {item} {item}: Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit habitant sed. ))} ); }; Chromatic.parameters = { chromatic: { disable: false } }; ================================================ FILE: apps/storybook/stories/alert-dialog.stories.module.css ================================================ .trigger { } .overlay, .overlayAttr { /* ensures overlay is positionned correctly */ position: fixed; inset: 0; /* --------- */ background-color: var(--gray-12); opacity: 0.2; } .content, .chromaticContent, .contentAttr { /* ensures good default position for content */ position: fixed; top: 0; left: 0; /* --------- */ top: 50%; left: 50%; transform: translate(-50%, -50%); background: var(--gray-1); min-width: 300px; min-height: 150px; padding: 50px; border-radius: 10px; background-color: var(--gray-1); box-shadow: 0 2px 10px var(--black-a6); } .cancel, .action { appearance: none; padding: 10px; border: none; } .cancel { background: var(--gray-3); color: var(--gray-12); } .action { background: var(--red-9); color: var(--gray-1); } .title { } .description { } .chromaticContent { padding: 10px; min-width: auto; min-height: auto; } .triggerAttr .overlayAttr, .contentAttr, .cancelAttr, .actionAttr, .titleAttr, .descriptionAttr { background-color: var(--blue-a12); border: 2px solid var(--blue-9); padding: 10px; &[data-state='closed'] { border-color: var(--red-9); } &[data-state='open'] { border-color: var(--green-9); } } ================================================ FILE: apps/storybook/stories/alert-dialog.stories.tsx ================================================ import * as React from 'react'; import { AlertDialog } from 'radix-ui'; import styles from './alert-dialog.stories.module.css'; export default { title: 'Components/AlertDialog' }; export const Styled = () => ( delete everything Are you sure? This will do a very dangerous thing. Thar be dragons! yolo, do it maybe not ); export const Controlled = () => { const [open, setOpen] = React.useState(false); const [housePurchased, setHousePurchased] = React.useState(false); return (
a large white house with a red roof
{ if (housePurchased) { e.preventDefault(); setHousePurchased(false); } }} > {housePurchased ? 'You bought the house! Sell it!' : 'Buy this house'} Are you sure? Houses are very expensive and it looks like you only have €20 in the bank. Maybe consult with a financial advisor? setHousePurchased(true)}> buy it anyway good point, I'll reconsider
); }; export const Chromatic = () => (

Uncontrolled

Closed

delete everything Title Description Confirm Cancel

Open

delete everything Title Description Confirm Cancel

Uncontrolled with reordered parts

Closed

Title Description Confirm Cancel delete everything

Open

Title Description Confirm Cancel delete everything

Controlled

Closed

delete everything Title Description Confirm Cancel

Open

delete everything Title Description Confirm Cancel

Controlled with reordered parts

Closed

Title Description Confirm Cancel delete everything

Open

Title Description Confirm Cancel delete everything

State attributes

Closed

delete everything Title Description Confirm Cancel

Open

delete everything Title Description Confirm Cancel
); Chromatic.parameters = { chromatic: { disable: false } }; ================================================ FILE: apps/storybook/stories/arrow.stories.tsx ================================================ import { Arrow } from 'radix-ui/internal'; export default { title: 'Utilities/Arrow' }; const RECOMMENDED_CSS__ARROW__ROOT = { // better default alignment verticalAlign: 'middle', }; export const Styled = () => ( ); export const CustomSizes = () => ( <> ); export const CustomArrow = () => (
); ================================================ FILE: apps/storybook/stories/aspect-ratio.stories.module.css ================================================ .root { display: flex; align-items: center; justify-content: center; background-color: var(--red-9); color: var(--gray-1); } ================================================ FILE: apps/storybook/stories/aspect-ratio.stories.tsx ================================================ import { AspectRatio } from 'radix-ui'; import styles from './aspect-ratio.stories.module.css'; export default { title: 'Components/AspectRatio' }; const image = ( A house in a forest ); export const Styled = () => (

Default ratio (1/1)

); export const CustomRatios = () => { return (
{image}
{image}
{image}
{image}
); }; export const Chromatic = () => ( <>

Default ratio

Default ratio (1/1)

Custom ratios

{image}
{image}
{image}
{image}
); Chromatic.parameters = { chromatic: { disable: false } }; ================================================ FILE: apps/storybook/stories/avatar.stories.module.css ================================================ .root { /* ensures image/fallback is centered */ display: inline-flex; align-items: center; justify-content: center; vertical-align: middle; /* ensures image doesn't bleed out */ overflow: hidden; /* ensures no selection is possible */ user-select: none; /* -------- */ border-radius: 9999px; width: 48px; height: 48px; } .image { /* ensures image is full size and not distorted */ width: 100%; height: 100%; object-fit: cover; } .fallback { /* ensures content inside the fallback is centered */ width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; /* -------- */ background-color: var(--gray-12); color: var(--gray-1); } ================================================ FILE: apps/storybook/stories/avatar.stories.tsx ================================================ import { Avatar } from 'radix-ui'; import styles from './avatar.stories.module.css'; import React from 'react'; export default { title: 'Components/Avatar' }; const src = 'https://picsum.photos/id/1005/400/400'; const srcAlternative = 'https://picsum.photos/id/1006/400/400'; const srcBroken = 'https://broken.link.com/broken-pic.jpg'; export const Styled = () => ( <>

Without image & with fallback

JS

With image & with fallback

JS

With image & with fallback (but broken src)

Changing image src

{(src) => ( JS )} ); export const Chromatic = () => ( <>

Without image & with fallback

JS

With image & with fallback

JS

With image & with fallback (but broken src)

Changing image src

{(src) => ( JS )} ); Chromatic.parameters = { chromatic: { disable: false, delay: 1000 } }; const AvatarIcon = () => ( ); function SourceChanger({ sources, children, }: { sources: [string, ...string[]]; children: (src: string) => React.ReactElement; }) { const [src, setSrc] = React.useState(sources[0]); React.useEffect(() => { const interval = setInterval(() => { const nextIndex = (sources.indexOf(src) + 1) % sources.length; setSrc(sources[nextIndex]!); }, 1000); return () => clearInterval(interval); }, [sources, src]); return children(src); } ================================================ FILE: apps/storybook/stories/checkbox.stories.module.css ================================================ .root { /* better default alignment */ vertical-align: middle; /* ------ */ border: 1px solid var(--gray-4); width: 30px; height: 30px; padding: 4px; &:focus { outline: none; border-color: var(--red-9); box-shadow: 0 0 0 1px var(--red-9); } &[data-disabled] { opacity: 0.3; } } .indicator { background-color: var(--red-9); display: block; width: 20px; height: 4px; &[data-state='checked'], &[data-state='unchecked'] { height: 20px; } } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } } .animatedIndicator { transition: height 300ms; &[data-state='checked'] { animation: fadeIn 1000ms ease-out; } &[data-state='unchecked'] { animation: fadeOut 1000ms ease-in; } } .rootAttr, .indicatorAttr { background-color: var(--blue-a12); border: 2px solid var(--blue-9); padding: 10px; &[data-state='unchecked'] { border-color: var(--red-9); } &[data-state='checked'] { border-color: var(--green-9); } &[data-state='indeterminate'] { border-color: var(--purple-9); } &[data-disabled] { border-style: dashed; } &:disabled { opacity: 0.5; } } .label { /* ensures it can receive vertical margins */ display: inline-block; /* better default alignment */ vertical-align: middle; /* mimics default `label` tag (as we render a `span`) */ cursor: default; display: inline-block; } ================================================ FILE: apps/storybook/stories/checkbox.stories.tsx ================================================ /* eslint-disable react/jsx-pascal-case */ import * as React from 'react'; import { Checkbox, Label as LabelPrimitive } from 'radix-ui'; import styles from './checkbox.stories.module.css'; export default { title: 'Components/Checkbox' }; export const Styled = () => ( <>

This checkbox is nested inside a label. The state is uncontrolled.

Custom label



Native label

Native label + native checkbox

Custom label + htmlFor



Native label + htmlFor

Native label + native checkbox

); export const Controlled = () => { const [checked, setChecked] = React.useState(true); return ( <>

This checkbox is placed adjacent to its label. The state is controlled.

{' '} ); }; export const Indeterminate = () => { const [checked, setChecked] = React.useState('indeterminate'); return ( <>

); }; export const WithinForm = () => { const [data, setData] = React.useState({ optional: false, required: false, stopprop: false }); const [checked, setChecked] = React.useState('indeterminate'); return (
event.preventDefault()} onChange={(event) => { const input = event.target as HTMLInputElement; setData((prevData) => ({ ...prevData, [input.name]: input.checked })); }} >
optional checked: {String(data.optional)}



required checked: {String(data.required)}


stop propagation checked: {String(data.stopprop)} event.stopPropagation()} >


no bubble input checked: {String(data.stopprop)}


); }; export const LegacyStyled = () => ( <>

This checkbox is nested inside a label. The state is uncontrolled.

Custom label



Native label

Native label + native checkbox

Custom label + htmlFor



Native label + htmlFor

Native label + native checkbox

); export const LegacyControlled = () => { const [checked, setChecked] = React.useState(true); return ( <>

This checkbox is placed adjacent to its label. The state is controlled.

{' '} ); }; export const LegacyIndeterminate = () => { const [checked, setChecked] = React.useState('indeterminate'); return ( <>

); }; export const LegacyWithinForm = () => { const [data, setData] = React.useState({ optional: false, required: false, stopprop: false }); const [checked, setChecked] = React.useState('indeterminate'); return (
event.preventDefault()} onChange={(event) => { const input = event.target as HTMLInputElement; setData((prevData) => ({ ...prevData, [input.name]: input.checked })); }} >
optional checked: {String(data.optional)}



required checked: {String(data.required)}


stop propagation checked: {String(data.stopprop)} event.stopPropagation()} >


); }; export const LegacyAnimated = () => { const [checked, setChecked] = React.useState('indeterminate'); return ( <>

); }; export const LegacyChromatic = () => ( <>

Uncontrolled

Unchecked

Checked

Controlled

Unchecked

Checked

Indeterminate

Disabled

Force mounted indicator

State attributes

Unchecked

Checked

Indeterminate

Disabled

Force mounted indicator

); LegacyChromatic.parameters = { chromatic: { disable: false } }; const Label = (props: any) => ; ================================================ FILE: apps/storybook/stories/collapsible.stories.module.css ================================================ .root { max-width: 20em; font-family: sans-serif; } .trigger { /* because it's a button, we want to stretch it */ width: 100%; /* and remove center text alignment in favour of inheriting */ text-align: inherit; /* ---- */ appearance: none; border: none; padding: 10px; background-color: var(--color-black); color: white; font-family: inherit; font-size: 1.2em; --shadow-color: crimson; &:focus { outline: none; box-shadow: inset 0 -5px 0 0 var(--shadow-color); color: var(--color-red); } &[data-disabled] { color: var(--color-gray300); } &[data-state='open'] { background-color: var(--color-red); color: var(--color-white); &:focus { --shadow-color: #111; color: var(--color-black); } } } .content { padding: 10px; line-height: 1.5; } @keyframes collapsible-slideDown { from { height: 0; } to { height: var(--radix-collapsible-content-height); } } @keyframes collapsible-slideUp { from { height: var(--radix-collapsible-content-height); } to { height: 0; } } @keyframes collapsible-openRight { from { width: 0; } to { width: var(--radix-collapsible-content-width); } } @keyframes collapsible-closeRight { from { width: var(--radix-collapsible-content-width); } to { width: 0; } } .animatedContent { overflow: hidden; &[data-state='open'] { animation: collapsible-slideDown 300ms ease-out; } &[data-state='closed'] { animation: collapsible-slideUp 300ms ease-in; } } .animatedWidthContent { overflow: hidden; &[data-state='open'] { animation: collapsible-openRight 300ms ease-out; } &[data-state='closed'] { animation: collapsible-closeRight 300ms ease-in; } } .rootAttr, .triggerAttr, .contentAttr { /* ensure we can see the content (because it has `hidden` attribute) */ display: block; background-color: rgb(0 0 255 / 0.3); border: 2px solid blue; padding: 10px; &[data-state='closed'] { border-color: red; } &[data-state='open'] { border-color: green; } &[data-disabled] { border-style: dashed; } &:disabled { opacity: 0.5; } } ================================================ FILE: apps/storybook/stories/collapsible.stories.tsx ================================================ import * as React from 'react'; import { Collapsible } from 'radix-ui'; import styles from './collapsible.stories.module.css'; export default { title: 'Components/Collapsible' }; export const Styled = () => ( Trigger Content 1 ); export const Controlled = () => { const [open, setOpen] = React.useState(false); return ( {open ? 'close' : 'open'}
Content 1
); }; export const Animated = () => { return ( <>

Closed by default

Trigger
Content 1

Open by default

Trigger
Content 1
); }; export const AnimatedHorizontal = () => { return ( Trigger
Content
); }; export const Chromatic = () => ( <>

Uncontrolled

Closed

Trigger Content 1

Open

Trigger Content 1

Controlled

Closed

Trigger Content 1

Open

Trigger Content 1

Disabled

Trigger Content 1

State attributes

Closed

Trigger Content 1

Open

Trigger Content 1

Disabled

Trigger Content 1 ); Chromatic.parameters = { chromatic: { disable: false } }; ================================================ FILE: apps/storybook/stories/collection.stories.tsx ================================================ import * as React from 'react'; import { Collection as CollectionPrimitive } from 'radix-ui/internal'; const { createCollection } = CollectionPrimitive; export default { title: 'Utilities/Collection' }; export const Basic = () => ( Red Green Blue ); export const WithElementInBetween = () => (
Colors
Red Green Blue
Words
Hello World
); const Tomato = () => Tomato; export const WithWrappedItem = () => ( Red Green Blue ); export const WithFragment = () => { const countries = ( <> France UK Spain ); return ( {countries} ); }; export const DynamicInsertion = () => { const [hasTomato, setHasTomato] = React.useState(false); const [, forceUpdate] = React.useState(); return ( <> ); }; function WrappedItems({ hasTomato }: any) { return ( <> Red {hasTomato ? : null} Green Blue ); } export const WithChangingItem = () => { const [isDisabled, setIsDisabled] = React.useState(false); return ( <> Red Green Blue ); }; export const Nested = () => ( 1 2 2.1 2.2 2.3 3 ); /* ------------------------------------------------------------------------------------------------- * List implementation * -----------------------------------------------------------------------------------------------*/ type ItemData = { disabled: boolean }; const [Collection, useCollection] = createCollection, ItemData>( 'List', ); const List: React.FC<{ children: React.ReactNode }> = (props) => { return (
    ); }; type ItemProps = React.ComponentPropsWithRef<'li'> & { children: React.ReactNode; disabled?: boolean; }; function Item({ disabled = false, ...props }: ItemProps) { return (
  • ); } // Ensure that our implementation doesn't break if the item list/item is memoized const MemoItem = React.memo(Item); const MemoItems = React.memo(WrappedItems); function LogItems({ name = 'items' }: { name?: string }) { const getItems = useCollection(undefined); React.useEffect(() => console.log(name, getItems())); return null; } ================================================ FILE: apps/storybook/stories/context-menu.stories.module.css ================================================ .trigger { display: flex; align-items: center; justify-content: center; width: 200px; height: 100px; border: 2px dashed var(--color-black); border-radius: 6px; background-color: rgba(0, 0, 0, 0.1); &:focus { outline: none; box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.5); } &[data-state='open'] { background-color: lightblue; } } .content { display: inline-block; box-sizing: border-box; min-width: 130px; background-color: var(--color-white); border: 1px solid var(--color-gray100); border-radius: 6px; padding: 5px; box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.1); font-family: apple-system, BlinkMacSystemFont, helvetica, arial, sans-serif; font-size: 13px; &:focus-within { border-color: var(--color-black); } } .animatedContent { transform-origin: var(--radix-context-menu-content-transform-origin); &[data-state='open'] { animation: contextMenu-scaleIn 0.6s cubic-bezier(0.16, 1, 0.3, 1); } } .label, .item, .subTrigger { display: flex; align-items: center; justify-content: space-between; line-height: 1; cursor: default; user-select: none; white-space: nowrap; height: 25px; padding: 0 10px; color: var(--color-black); border-radius: 3px; } .label { color: var(--color-gray100); } .item, .subTrigger { outline: none; &[data-highlighted] { background-color: var(--color-black); color: var(--color-white); } &[data-disabled] { color: var(--color-gray100); } } .subTrigger { &:not([data-highlighted])[data-state='open'] { background-color: var(--color-gray100); color: var(--color-black); } } .separator { height: 1; margin: 5px 10px; background-color: var(--color-gray100); } @keyframes contextMenu-scaleIn { 0% { transform: scale(0) rotateZ(-10deg); } 20% { transform: scale(1.1); } 100% { transform: scale(1); } } ================================================ FILE: apps/storybook/stories/context-menu.stories.tsx ================================================ import * as React from 'react'; import { ContextMenu } from 'radix-ui'; import { foodGroups } from '@repo/test-data/foods'; import styles from './context-menu.stories.module.css'; export default { title: 'Components/ContextMenu' }; export const Styled = () => (
    Right click here console.log('undo')}> Undo console.log('redo')}> Redo console.log('cut')}> Cut console.log('copy')}> Copy console.log('paste')}> Paste
    ); export const Modality = () => (

    Modal (default)

    console.log('undo')}> Undo console.log('redo')}> Redo Submenu → console.log('one')}> One console.log('two')}> Two Submenu → console.log('one')} > One console.log('two')} > Two console.log('three')} > Three console.log('three')}> Three console.log('cut')} > Cut console.log('copy')}> Copy console.log('paste')}> Paste