diff --git a/.circleci/config.yml b/.circleci/config.yml index 44a3e0e7ed2..c6c4212f203 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -66,8 +66,18 @@ jobs: - run: name: Run tests on JSDOM command: pnpm test:jsdom:coverage + - store_test_results: + path: test-results - code-infra/upload-coverage: key: << parameters.react-version >>-jsdom + docs_validate: + <<: *default-job + steps: + - checkout + - code-infra/install-deps + - run: + name: Validate the documentation files + command: pnpm docs:validate test_lint: <<: *default-job steps: @@ -91,11 +101,6 @@ jobs: command: | pnpm extract-error-codes git add -A && git diff --exit-code --staged - - run: - name: '`pnpm docs:link-check` changes committed?' - command: | - pnpm docs:link-check - git add -A && git diff --exit-code --staged - run: name: '`pnpm inline-scripts` changes committed?' command: | @@ -160,6 +165,8 @@ jobs: - run: name: Run tests on headless Chromium command: pnpm test:chromium --coverage + - store_test_results: + path: test-results - code-infra/upload-coverage: key: << parameters.react-version >>-browser test_regressions: @@ -173,6 +180,8 @@ jobs: - run: name: Run visual regression tests command: xvfb-run pnpm test:regressions + - store_test_results: + path: test-results - run: name: Upload screenshots to Argos CI command: pnpm test:argos @@ -187,6 +196,8 @@ jobs: - run: name: pnpm test:e2e command: pnpm test:e2e + - store_test_results: + path: test-results test_package: <<: *default-job @@ -221,6 +232,8 @@ jobs: command: pnpm test:jsdom:coverage environment: MUI_DISABLE_WORKSPACE_ALIASES: 1 + - store_test_results: + path: test-results test_browser_compiler: <<: *default-job @@ -236,6 +249,8 @@ jobs: command: pnpm test:chromium --coverage environment: MUI_DISABLE_WORKSPACE_ALIASES: 1 + - store_test_results: + path: test-results workflows: pipeline: @@ -266,6 +281,9 @@ workflows: - test_package: <<: *default-context name: 'Package verification' + - docs_validate: + <<: *default-context + name: 'Validating Docs' - test_unit_compiler: <<: *default-context name: 'JSDOM tests (React Compiler)' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b262e601d4b..04ead76d5a6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,14 +22,14 @@ jobs: os: [macos-latest, windows-latest, ubuntu-latest] steps: - run: echo "${{ github.actor }}" - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: # fetch all tags which are required for `pnpm release:changelog` fetch-depth: 0 - name: Set up pnpm uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Use Node.js 22.x - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: node-version: '22.18' cache: 'pnpm' # https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#caching-packages-dependencies diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index b8471b3bbb4..3cb73ef06e0 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -16,10 +16,10 @@ jobs: security-events: write steps: - name: Checkout repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5 + uses: github/codeql-action/init@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7 with: languages: typescript config-file: ./.github/codeql/codeql-config.yml @@ -30,4 +30,4 @@ jobs: # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5 + uses: github/codeql-action/analyze@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7 diff --git a/.github/workflows/fixed-issue.yml b/.github/workflows/fixed-issue.yml index f930110a140..fda9ef1df3d 100644 --- a/.github/workflows/fixed-issue.yml +++ b/.github/workflows/fixed-issue.yml @@ -130,7 +130,7 @@ jobs: In the meantime, you can try it out on our Canary release channel: \`\`\`sh - npm i https://pkg.pr.new/@base-ui-components/react@${prNumber} + npm i https://pkg.pr.new/@base-ui/react@${prNumber} \`\`\``; await github.rest.issues.createComment({ diff --git a/.github/workflows/mark-duplicate.yml b/.github/workflows/mark-duplicate.yml index aa220cd2387..272f974c087 100644 --- a/.github/workflows/mark-duplicate.yml +++ b/.github/workflows/mark-duplicate.yml @@ -14,7 +14,7 @@ jobs: issues: write steps: - name: Mark duplicate - uses: actions-cool/issues-helper@9861779a695cf1898bd984c727f685f351cfc372 # v3.7.2 + uses: actions-cool/issues-helper@3809910bc12872edc9b8132f122069ac16cd16ee # v3.7.3 with: actions: 'mark-duplicate' token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7593cb1e7b1..c781f021458 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -35,7 +35,7 @@ jobs: name: npm-publish steps: - name: Checkout - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ inputs.sha }} fetch-depth: 0 # Fetch full history for proper git operations diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 51d777b3d6c..e3d168d24c0 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -22,7 +22,7 @@ jobs: actions: read steps: - name: Checkout code - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Run analysis @@ -40,6 +40,6 @@ jobs: publish_results: true # Upload the results to GitHub's code scanning dashboard. - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5 + uses: github/codeql-action/upload-sarif@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7 with: sarif_file: results.sarif diff --git a/.github/workflows/vale-action.yml b/.github/workflows/vale-action.yml index 6c40a07fc0d..249daccb417 100644 --- a/.github/workflows/vale-action.yml +++ b/.github/workflows/vale-action.yml @@ -12,7 +12,7 @@ jobs: contents: read pull-requests: write steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Extract Vale version from pnpm-lock.yaml id: vale-version run: | diff --git a/.vale.ini b/.vale.ini index 930f7871340..e7683d8dbd6 100644 --- a/.vale.ini +++ b/.vale.ini @@ -12,7 +12,7 @@ Packages = Google, https://github.com/mui/material-ui/raw/HEAD/docs/mui-vale.zip [formats] mdx = md -[*.md] +[*.{md,mdx}] # Ignore React component calls TokenIgnores = (<\/?[A-Z].+>) @@ -29,5 +29,9 @@ Google.We = YES # Avoid first-person plural Google.Will = YES # Avoid future tense Google.OxfordComma = YES # Prefer Oxford comma +[*.mdx] +# MDX doesn't support HTML comments, see https://vale.sh/docs/keys/commentdelimiters. +CommentDelimiters = {/*, */} + [CHANGELOG*.md] MUI.CorrectReferenceAllCases = NO diff --git a/AGENTS.md b/AGENTS.md index 77125c82931..eb25a949447 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,14 +7,14 @@ This repository contains the source code and documentation for Base UI: a headl - Source code for components and private utils is in `packages/react/`. - Source code for public shared utils is in `packages/utils/`. - Experiments are located at `docs/src/app/(private)/experiments/`. Use for creating demos that require manual testing in the browser. -- Public documentation is located at `docs/src/app/(public)/(content)/react/`. Alter the docs where necessary when changes must be visible to library users. +- Public documentation is located at `docs/src/app/(docs)/react/`. Alter the docs where necessary when changes must be visible to library users. - When creating public demos on the docs, refer to the `hero` demo for the given component and largely follow its styles (both CSS Modules and Tailwind CSS versions). Other demos may also contain relevant styling. Do not add custom styling beyond the critical layout styles necessary for new demos. ## Code guidelines -- Always use the `useTimeout` utility from `@base-ui-components/utils/useTimeout` instead of `window.setTimeout`, and `useAnimationFrame` from `@base-ui-components/utils/useAnimationFrame` instead of `requestAnimationFrame`. Search for other example usage in the codebase if unsure how to use them. -- Use the `useStableCallback` utility from `@base-ui-components/utils/useStableCallback` instead of `React.useCallback` if the function is called within an effect or event handler. The utility cannot be used to memoize functions that are called directly in the body of a component (during render), so continue with `React.useCallback` in those scenarios. -- Always use the `useIsoLayoutEffect` utility from `@base-ui-components/utils/useIsoLayoutEffect` instead of `React.useLayoutEffect`. +- Always use the `useTimeout` utility from `@base-ui/utils/useTimeout` instead of `window.setTimeout`, and `useAnimationFrame` from `@base-ui/utils/useAnimationFrame` instead of `requestAnimationFrame`. Search for other example usage in the codebase if unsure how to use them. +- Use the `useStableCallback` utility from `@base-ui/utils/useStableCallback` instead of `React.useCallback` if the function is called within an effect or event handler. The utility cannot be used to memoize functions that are called directly in the body of a component (during render), so continue with `React.useCallback` in those scenarios. +- Always use the `useIsoLayoutEffect` utility from `@base-ui/utils/useIsoLayoutEffect` instead of `React.useLayoutEffect`. - Avoid duplicating logic where necessary. If two components can share logic (such as event handlers), define the logic/handlers in the parent and share it through a context to the child; use the existing context if it exists. ## Linting, typechecking, and formatting diff --git a/CHANGELOG.md b/CHANGELOG.md index 18b561e3805..4ef6f6eaba6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,159 @@ # Versions +## v1.0.0 + +_Dec 11, 2025_ + +### General Changes + +- **Breaking change:** Rename packages to use the `@base-ui` org.
+ The package name has changed from `@base-ui-components/react` to `@base-ui/react`. + (#3462) by @mnajdova + +### Combobox + +- Respect `itemToStringValue` for `onFormSubmit` (#3441) by @atomiks +- Add `null` as an option for the value prop (#3488) by @mnajdova + +### Menu + +- Fix submenu opens with 0 delay (#3459) by @atomiks +- Fix focus not returning to trigger on Esc while pointer rests on popup (#3482) by @atomiks +- Fix always `null` open method (#3486) by @atomiks +- Allow side axis fallback for submenus by default (#3470) by @atomiks + +### Navigation Menu + +- Fix mount transitions on `Positioner` in Firefox (#3424) by @atomiks + +### Number Field + +- Fix multiple scrub area support (#3471) by @atomiks + +### Popover + +- Fix mount transitions on `Positioner` in Firefox (#3424) by @atomiks +- Fix skipped viewport transitions (#3453) by @atomiks + +### Select + +- Respect `itemToStringValue` for `onFormSubmit` (#3441) by @atomiks +- Add `null` as an option for the value prop (#3488) by @mnajdova + +### Tabs + +- Fix indicator positioning in transformed containers (#3439) by @atomiks +- Do not initially select a disabled tab (#3475) by @michaldudak + +### Toast + +- Fix `flushSync` dev error when toast is added (#3443) by @atomiks +- Fix `;` emitting `aria-hidden` warning on click (#3469) by @atomiks + +### Toggle Group + +- More permissive towards falsy toggle values (#3477) by @mj12albert + +### Tooltip + +- Fix mount transitions on `Positioner` in Firefox (#3424) by @atomiks +- Fix ignored "modal" setting in Popovers experiment (#3474) by @michaldudak +- Fix shared tooltip closing with trigger gaps (#3452) by @atomiks +- Fix skipped viewport transitions (#3453) by @atomiks + +All contributors of this release in alphabetical order: @atomiks, @LukasTy, @michaldudak, @mj12albert, @mnajdova, @oliviertassinari, @pondorasti, @romgrk, @ZeeshanTamboli + +## v1.0.0-rc.2 + +_Dec 11, 2025_ + +This release contains the same code as v1.0.0. +Please refer to that version to see the changes. + +## v1.0.0-rc.1 + +_Dec 11, 2025_ + +This release contains the same code as v1.0.0. +Please refer to that version to see the changes. + +## v1.0.0-rc.0 + +_Dec 4, 2025_ + +### General changes + +- Fix missing `'use client'` directives (#3408) by @atomiks + +### Autocomplete + +- Fix `keepHighlight` focus sync (#3399) by @atomiks + +### Checkbox + +- **Breaking change:** Match native unchecked state in form submission.
+ The Checkbox will not submit the `"off"` value with a form when unchecked anymore, unless the new `uncheckedValue` prop is set. + (#3406) by @atomiks + +### Collapsible + +- Remove `render={null}` (#3407) by @mj12albert + +### Combobox + +- **Breaking change:** Removed the `keepHighlight` prop (#3377) by @atomiks + +### Dialog + +- Close when pressing focusable element outside (#3380) by @atomiks +- Fix closing after pointer lock exit in Firefox (#3379) by @atomiks + +### Menu + +- Add `highlightItemOnHover` prop (#3377) by @atomiks +- Do not import client components from MenuStore (#3409) by @michaldudak + +### Number Field + +- Ensure hidden input participates in form validation (#3374) by @atomiks +- Improve symbol replacement logic (#3376) by @atomiks +- Fix fractional step snapping (#3375) by @atomiks +- Fix parsing numbers with Swiss locale (#3361) by @michaldudak +- Fix pointer lock release when soft clicking in Firefox (#3378) by @atomiks + +### Popover + +- Close when pressing focusable element outside (#3380) by @atomiks +- Fix modal backdrop on touch (#3383) by @atomiks +- Fix popover glitching when flipped (#3364) by @michaldudak + +### Select + +- Add `highlightItemOnHover` prop (#3377) by @atomiks + +### Switch + +- **Breaking change:** Match native off state in form submission.
+ The Switch will not submit the `"off"` value with a form when unchecked anymore, unless the new `uncheckedValue` prop is set. + (#3406) by @atomiks + +### Tabs + +- **Breaking change:** Fix Panel `keepMounted` behavior.
+ The `value` prop is now required on `` and `` parts. + (#3372) by @atomiks + +### Toast + +- Recalculate content height when layout size is fixed (#3359) by @atomiks +- Fix multiple swipe directions on same axis (#3392) by @mj12albert + +### Tooltip + +- Improve contained triggers performance (#3385) by @michaldudak + +All contributors of this release in alphabetical order: @atomiks, @michaldudak, @mj12albert, @oliviertassinari, @pondorasti, @romgrk + ## v1.0.0-beta.7 _Nov 27, 2025_ diff --git a/docs/.link-check-errors.txt b/docs/.link-check-errors.txt deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/docs/README.md b/docs/README.md index 9b48e080258..aea460b099e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -10,7 +10,7 @@ pnpm start If you do not have pnpm installed, select your OS and follow the instructions on the [pnpm website](https://pnpm.io/installation). -Package managers other than pnpm (like npm or Yarn) are not supported and will not work. +Package managers other than pnpm (like npm or Yarn) are not supported and don't work. ## How can I add a new demo to the documentation? diff --git a/docs/next.config.mjs b/docs/next.config.mjs index 7279c3bddb5..401d5dff6f4 100644 --- a/docs/next.config.mjs +++ b/docs/next.config.mjs @@ -3,6 +3,8 @@ import * as path from 'path'; import * as url from 'url'; import * as fs from 'fs'; import { withDeploymentConfig } from '@mui/internal-docs-infra/withDocsInfra'; +import transformMarkdownMetadata from '@mui/internal-docs-infra/pipeline/transformMarkdownMetadata'; +import transformMarkdownRelativePaths from '@mui/internal-docs-infra/pipeline/transformMarkdownRelativePaths'; import nextMdx from '@next/mdx'; import rehypeExtractToc from '@stefanprobst/rehype-extract-toc'; import remarkGfm from 'remark-gfm'; @@ -20,7 +22,29 @@ const workspaceRoot = path.resolve(currentDirectory, '../'); const withMdx = nextMdx({ options: { - remarkPlugins: [remarkGfm, remarkTypography], + remarkPlugins: [ + remarkGfm, + [ + transformMarkdownMetadata, + { + titleSuffix: ' · Base UI', + extractToIndex: { + include: ['src/app/react'], + exclude: [ + 'src/app/careers', + 'src/app/production-error', + 'src/app/test', + 'src/app/experiments', + 'src/app/playground', + ], + baseDir: path.dirname(url.fileURLToPath(import.meta.url)), + useVisibleDescription: true, + }, + }, + ], + remarkTypography, + transformMarkdownRelativePaths, + ], rehypePlugins: [ rehypeReference, ...rehypeSyntaxHighlighting, @@ -50,6 +74,10 @@ const nextConfig = { pageExtensions: ['mdx', 'tsx'], turbopack: { rules: { + './src/app/sitemap/index.ts': { + as: '*.ts', + loaders: ['@mui/internal-docs-infra/pipeline/loadPrecomputedSitemap'], + }, './src/app/**/demos/*/index.ts': { as: '*.ts', loaders: ['@mui/internal-docs-infra/pipeline/loadPrecomputedCodeHighlighter'], @@ -63,14 +91,18 @@ const nextConfig = { webpack: (config, { defaultLoaders }) => { // for production builds config.module.rules.push({ - test: /\/demos\/[^/]+\/index\.ts$/, + test: /[/\\\\]sitemap[/\\\\]index\.ts$/, + use: [defaultLoaders.babel, '@mui/internal-docs-infra/pipeline/loadPrecomputedSitemap'], + }); + config.module.rules.push({ + test: /[/\\\\]demos[/\\\\][^/\\\\]+[/\\\\]index\.ts$/, use: [ defaultLoaders.babel, '@mui/internal-docs-infra/pipeline/loadPrecomputedCodeHighlighter', ], }); config.module.rules.push({ - test: /\/src\/demo-data\/[^/]+\/index\.ts$/, + test: /[/\\\\]src[/\\\\]demo-data[/\\\\][^/\\\\]+[/\\\\]index\.ts$/, use: [ defaultLoaders.babel, '@mui/internal-docs-infra/pipeline/loadPrecomputedCodeHighlighter', diff --git a/docs/package.json b/docs/package.json index 10cb590c410..7b1c2a2422a 100644 --- a/docs/package.json +++ b/docs/package.json @@ -3,30 +3,33 @@ "private": true, "license": "MIT", "scripts": { - "build": "rimraf ./export && cross-env NODE_ENV=production NODE_OPTIONS=--max_old_space_size=8192 next build --webpack", + "build": "rimraf ./export && cross-env NODE_ENV=production NODE_OPTIONS=--max_old_space_size=8192 next build --webpack && pnpm link-check", "build:clean": "rimraf .next && pnpm build", "dev": "next dev --webpack --port 3005", "deploy": "git fetch upstream master && git push -f upstream FETCH_HEAD:docs-v1", "serve": "serve ./export -l 3010", + "docs-infra": "docs-infra", + "validate": "pnpm internal-validate --command 'pnpm validate'", + "internal-validate": "docs-infra validate --useVisibleDescription", "typescript": "tsc -b tsconfig.json", "link-check": "tsx ./scripts/reportBrokenLinks.mts", "generate-llms": "node ./scripts/generateLlmTxt/index.mjs" }, "dependencies": { - "@base-ui-components/react": "workspace:*", - "@base-ui-components/utils": "workspace:*", + "@base-ui/react": "workspace:*", + "@base-ui/utils": "workspace:*", "@emotion/cache": "^11.14.0", "@emotion/react": "^11.14.0", "@emotion/server": "^11.11.0", "@emotion/styled": "^11.14.1", "@mdx-js/loader": "^3.1.1", "@mdx-js/react": "^3.1.1", - "@mui/internal-docs-infra": "^0.2.3-canary.15", - "@next/mdx": "^16.0.4", + "@mui/internal-docs-infra": "0.3.1-canary.1", + "@next/mdx": "^16.0.8", "@react-spring/web": "^10.0.3", "@stefanprobst/rehype-extract-toc": "^3.0.0", - "@tanstack/react-form": "^1.25.0", - "@tanstack/react-virtual": "^3.13.12", + "@tanstack/react-form": "^1.27.1", + "@tanstack/react-virtual": "^3.13.13", "@types/mdx": "^2.0.13", "clipboard-copy": "^4.0.1", "clsx": "^2.1.1", @@ -36,18 +39,19 @@ "hast": "^1.0.0", "hast-util-heading-rank": "^3.0.0", "hast-util-to-string": "^3.0.1", - "lucide-react": "^0.554.0", + "lucide-react": "^0.556.0", "lz-string": "^1.5.0", "match-sorter": "^8.2.0", - "next": "16.0.4", + "next": "16.0.9", "postcss": "^8.5.6", "postcss-custom-media": "^11.0.6", "postcss-import": "^16.1.1", - "react": "^19.2.0", - "react-dom": "^19.2.0", + "radix-ui": "^1.4.3", + "react": "^19.2.1", + "react-dom": "^19.2.1", "react-error-boundary": "^6.0.0", - "react-hook-form": "^7.66.1", - "react-is": "^19.2.0", + "react-hook-form": "^7.68.0", + "react-is": "^19.2.1", "rehype-pretty-code": "^0.14.1", "remark": "^15.0.1", "remark-frontmatter": "^5.0.0", @@ -58,14 +62,14 @@ "remark-typography": "0.7.0", "scroll-into-view-if-needed": "3.1.0", "server-only": "^0.0.1", - "shiki": "^3.15.0", + "shiki": "^3.19.0", "to-vfile": "^8.0.0", "unist-util-visit-parents": "^6.0.2", "vfile-matter": "^5.0.1" }, "devDependencies": { "@mdx-js/mdx": "^3.1.1", - "@mui/internal-test-utils": "^2.0.15", + "@mui/internal-test-utils": "2.0.15", "@tailwindcss/postcss": "4.1.17", "@types/chai": "^5.2.3", "@types/gtag.js": "^0.0.20", @@ -78,8 +82,8 @@ "chai": "^6.2.1", "cross-env": "^10.1.0", "mdast-util-mdx-jsx": "^3.2.0", - "motion": "^12.23.24", - "prettier": "^3.6.2", + "motion": "^12.23.25", + "prettier": "~3.7.4", "react-reconciler": "^0.33.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", diff --git a/docs/reference/generated/accordion-root.json b/docs/reference/generated/accordion-root.json index 8a20e351a40..647038c9956 100644 --- a/docs/reference/generated/accordion-root.json +++ b/docs/reference/generated/accordion-root.json @@ -31,7 +31,7 @@ }, "multiple": { "type": "boolean", - "default": "true", + "default": "false", "description": "Whether multiple items can be open at the same time.", "detailedType": "boolean | undefined" }, diff --git a/docs/reference/generated/alert-dialog-root.json b/docs/reference/generated/alert-dialog-root.json index 95e4fd26688..afdeb51f8f1 100644 --- a/docs/reference/generated/alert-dialog-root.json +++ b/docs/reference/generated/alert-dialog-root.json @@ -20,7 +20,7 @@ }, "actionsRef": { "type": "RefObject", - "description": "A ref to imperative actions.\n- `unmount`: When specified, the dialog will not be unmounted when closed.\nInstead, the `unmount` function must be called to unmount the dialog manually.\nUseful when the dialog's animation is controlled by an external library.", + "description": "A ref to imperative actions.\n- `unmount`: When specified, the dialog will not be unmounted when closed.\nInstead, the `unmount` function must be called to unmount the dialog manually.\nUseful when the dialog's animation is controlled by an external library.\n- `close`: Closes the dialog imperatively when called.", "detailedType": "| React.RefObject\n| undefined" }, "defaultTriggerId": { diff --git a/docs/reference/generated/autocomplete-root.json b/docs/reference/generated/autocomplete-root.json index 99ec88acc31..ddda78ab5a1 100644 --- a/docs/reference/generated/autocomplete-root.json +++ b/docs/reference/generated/autocomplete-root.json @@ -53,7 +53,7 @@ "highlightItemOnHover": { "type": "boolean", "default": "true", - "description": "Whether moving the pointer over items should highlight them.", + "description": "Whether moving the pointer over items should highlight them.\nDisabling this prop allows CSS `:hover` to be differentiated from the `:focus` (`data-highlighted`) state.", "detailedType": "boolean | undefined" }, "actionsRef": { diff --git a/docs/reference/generated/checkbox-root.json b/docs/reference/generated/checkbox-root.json index a6f34f403e3..2291bfd0b12 100644 --- a/docs/reference/generated/checkbox-root.json +++ b/docs/reference/generated/checkbox-root.json @@ -48,6 +48,11 @@ "description": "Whether the checkbox controls a group of child checkboxes.\n\nMust be used in a [Checkbox Group](https://base-ui.com/react/components/checkbox-group).", "detailedType": "boolean | undefined" }, + "uncheckedValue": { + "type": "string", + "description": "The value submitted with the form when the checkbox is unchecked.\nBy default, unchecked checkboxes do not submit any value, matching native checkbox behavior.", + "detailedType": "string | undefined" + }, "disabled": { "type": "boolean", "default": "false", diff --git a/docs/reference/generated/collapsible-root.json b/docs/reference/generated/collapsible-root.json index 6d49f32495a..5fdb1376d98 100644 --- a/docs/reference/generated/collapsible-root.json +++ b/docs/reference/generated/collapsible-root.json @@ -34,8 +34,9 @@ "detailedType": "| React.CSSProperties\n| ((\n state: Collapsible.Root.State,\n ) => CSSProperties | undefined)\n| undefined" }, "render": { - "type": "ReactElement | ((props: HTMLProps, state: Collapsible.Root.State) => ReactElement) | null", - "detailedType": "| ReactElement\n| ((\n props: HTMLProps,\n state: Collapsible.Root.State,\n ) => ReactElement)\n| null" + "type": "ReactElement | ((props: HTMLProps, state: Collapsible.Root.State) => ReactElement)", + "description": "Allows you to replace the component’s HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render.", + "detailedType": "| ReactElement\n| ((\n props: HTMLProps,\n state: Collapsible.Root.State,\n ) => ReactElement)" } }, "dataAttributes": {}, diff --git a/docs/reference/generated/combobox-root.json b/docs/reference/generated/combobox-root.json index c10fdabdb83..c72a10242c5 100644 --- a/docs/reference/generated/combobox-root.json +++ b/docs/reference/generated/combobox-root.json @@ -13,9 +13,9 @@ "detailedType": "Value[] | Value | null | undefined" }, "value": { - "type": "Value[] | Value", + "type": "Value[] | Value | null", "description": "The selected value of the combobox. Use when controlled.", - "detailedType": "Value[] | Value | undefined" + "detailedType": "Value[] | Value | null | undefined" }, "onValueChange": { "type": "((value: Value[] | Value | any | null, eventDetails: Combobox.Root.ChangeEventDetails) => void)", @@ -59,16 +59,10 @@ "description": "Whether the first matching item is highlighted automatically while filtering.", "detailedType": "boolean | undefined" }, - "keepHighlight": { - "type": "boolean", - "default": "false", - "description": "Whether the highlighted item should be preserved when the pointer leaves the list.", - "detailedType": "boolean | undefined" - }, "highlightItemOnHover": { "type": "boolean", "default": "true", - "description": "Whether moving the pointer over items should highlight them.", + "description": "Whether moving the pointer over items should highlight them.\nDisabling this prop allows CSS `:hover` to be differentiated from the `:focus` (`data-highlighted`) state.", "detailedType": "boolean | undefined" }, "actionsRef": { diff --git a/docs/reference/generated/context-menu-root.json b/docs/reference/generated/context-menu-root.json index 2a95de1c083..7a0e8e86bf0 100644 --- a/docs/reference/generated/context-menu-root.json +++ b/docs/reference/generated/context-menu-root.json @@ -18,6 +18,12 @@ "description": "Event handler called when the menu is opened or closed.", "detailedType": "| ((\n open: boolean,\n eventDetails: ContextMenu.Root.ChangeEventDetails,\n ) => void)\n| undefined" }, + "highlightItemOnHover": { + "type": "boolean", + "default": "true", + "description": "Whether moving the pointer over items should highlight them.\nDisabling this prop allows CSS `:hover` to be differentiated from the `:focus` (`data-highlighted`) state.", + "detailedType": "boolean | undefined" + }, "actionsRef": { "type": "RefObject", "description": "A ref to imperative actions.\n- `unmount`: When specified, the menu will not be unmounted when closed.\n Instead, the `unmount` function must be called to unmount the menu manually.\n Useful when the menu's animation is controlled by an external library.\n- `close`: When specified, the menu can be closed imperatively.", @@ -25,7 +31,7 @@ }, "closeParentOnEsc": { "type": "boolean", - "default": "true", + "default": "false", "description": "When in a submenu, determines whether pressing the Escape key\ncloses the entire menu, or only the current child menu.", "detailedType": "boolean | undefined" }, diff --git a/docs/reference/generated/dialog-root.json b/docs/reference/generated/dialog-root.json index af2c4f583a4..86768b3f2ec 100644 --- a/docs/reference/generated/dialog-root.json +++ b/docs/reference/generated/dialog-root.json @@ -20,7 +20,7 @@ }, "actionsRef": { "type": "RefObject", - "description": "A ref to imperative actions.\n- `unmount`: When specified, the dialog will not be unmounted when closed.\nInstead, the `unmount` function must be called to unmount the dialog manually.\nUseful when the dialog's animation is controlled by an external library.", + "description": "A ref to imperative actions.\n- `unmount`: When specified, the dialog will not be unmounted when closed.\nInstead, the `unmount` function must be called to unmount the dialog manually.\nUseful when the dialog's animation is controlled by an external library.\n- `close`: Closes the dialog imperatively when called.", "detailedType": "React.RefObject | undefined" }, "defaultTriggerId": { diff --git a/docs/reference/generated/menu-checkbox-item.json b/docs/reference/generated/menu-checkbox-item.json index cbe6718fef7..c6b755120a2 100644 --- a/docs/reference/generated/menu-checkbox-item.json +++ b/docs/reference/generated/menu-checkbox-item.json @@ -46,10 +46,6 @@ "description": "Whether the component should ignore user interaction.", "detailedType": "boolean | undefined" }, - "id": { - "type": "string", - "detailedType": "string | undefined" - }, "className": { "type": "string | ((state: Menu.CheckboxItem.State) => string | undefined)", "description": "CSS class applied to the element, or a function that\nreturns a class based on the component’s state.", diff --git a/docs/reference/generated/menu-item.json b/docs/reference/generated/menu-item.json index a44b7c938d4..3a45faffc02 100644 --- a/docs/reference/generated/menu-item.json +++ b/docs/reference/generated/menu-item.json @@ -30,10 +30,6 @@ "description": "Whether the component should ignore user interaction.", "detailedType": "boolean | undefined" }, - "id": { - "type": "string", - "detailedType": "string | undefined" - }, "className": { "type": "string | ((state: Menu.Item.State) => string | undefined)", "description": "CSS class applied to the element, or a function that\nreturns a class based on the component’s state.", diff --git a/docs/reference/generated/menu-popup.json b/docs/reference/generated/menu-popup.json index 9b53e8f7094..afefee7fb07 100644 --- a/docs/reference/generated/menu-popup.json +++ b/docs/reference/generated/menu-popup.json @@ -7,10 +7,6 @@ "description": "Determines the element to focus when the menu is closed.\n\n- `false`: Do not move focus.\n- `true`: Move focus based on the default behavior (trigger or previously focused element).\n- `RefObject`: Move focus to the ref element.\n- `function`: Called with the interaction type (`mouse`, `touch`, `pen`, or `keyboard`).\n Return an element to focus, `true` to use the default behavior, or `false`/`undefined` to do nothing.", "detailedType": "| boolean\n| React.RefObject\n| ((\n closeType: InteractionType,\n ) => boolean | void | HTMLElement | null)\n| undefined" }, - "id": { - "type": "string", - "detailedType": "string | undefined" - }, "children": { "type": "ReactNode", "detailedType": "React.ReactNode" diff --git a/docs/reference/generated/menu-radio-item.json b/docs/reference/generated/menu-radio-item.json index c8199b016fd..0262208958b 100644 --- a/docs/reference/generated/menu-radio-item.json +++ b/docs/reference/generated/menu-radio-item.json @@ -35,10 +35,6 @@ "description": "Whether the component should ignore user interaction.", "detailedType": "boolean | undefined" }, - "id": { - "type": "string", - "detailedType": "string | undefined" - }, "className": { "type": "string | ((state: Menu.RadioItem.State) => string | undefined)", "description": "CSS class applied to the element, or a function that\nreturns a class based on the component’s state.", diff --git a/docs/reference/generated/menu-root.json b/docs/reference/generated/menu-root.json index b28ae96dd1e..b16b75c715d 100644 --- a/docs/reference/generated/menu-root.json +++ b/docs/reference/generated/menu-root.json @@ -18,6 +18,12 @@ "description": "Event handler called when the menu is opened or closed.", "detailedType": "| ((\n open: boolean,\n eventDetails: Menu.Root.ChangeEventDetails,\n ) => void)\n| undefined" }, + "highlightItemOnHover": { + "type": "boolean", + "default": "true", + "description": "Whether moving the pointer over items should highlight them.\nDisabling this prop allows CSS `:hover` to be differentiated from the `:focus` (`data-highlighted`) state.", + "detailedType": "boolean | undefined" + }, "actionsRef": { "type": "RefObject", "description": "A ref to imperative actions.\n- `unmount`: When specified, the menu will not be unmounted when closed.\n Instead, the `unmount` function must be called to unmount the menu manually.\n Useful when the menu's animation is controlled by an external library.\n- `close`: When specified, the menu can be closed imperatively.", @@ -25,7 +31,7 @@ }, "closeParentOnEsc": { "type": "boolean", - "default": "true", + "default": "false", "description": "When in a submenu, determines whether pressing the Escape key\ncloses the entire menu, or only the current child menu.", "detailedType": "boolean | undefined" }, diff --git a/docs/reference/generated/menu-submenu-root.json b/docs/reference/generated/menu-submenu-root.json index 42e0c239b0e..9cb4a628963 100644 --- a/docs/reference/generated/menu-submenu-root.json +++ b/docs/reference/generated/menu-submenu-root.json @@ -18,6 +18,12 @@ "description": "Event handler called when the menu is opened or closed.", "detailedType": "| ((\n open: boolean,\n eventDetails: Menu.SubmenuRoot.ChangeEventDetails,\n ) => void)\n| undefined" }, + "highlightItemOnHover": { + "type": "boolean", + "default": "true", + "description": "Whether moving the pointer over items should highlight them.\nDisabling this prop allows CSS `:hover` to be differentiated from the `:focus` (`data-highlighted`) state.", + "detailedType": "boolean | undefined" + }, "actionsRef": { "type": "RefObject", "description": "A ref to imperative actions.\n- `unmount`: When specified, the menu will not be unmounted when closed.\n Instead, the `unmount` function must be called to unmount the menu manually.\n Useful when the menu's animation is controlled by an external library.\n- `close`: When specified, the menu can be closed imperatively.", @@ -25,7 +31,7 @@ }, "closeParentOnEsc": { "type": "boolean", - "default": "true", + "default": "false", "description": "When in a submenu, determines whether pressing the Escape key\ncloses the entire menu, or only the current child menu.", "detailedType": "boolean | undefined" }, diff --git a/docs/reference/generated/menu-submenu-trigger.json b/docs/reference/generated/menu-submenu-trigger.json index 84f958b89da..44932492368 100644 --- a/docs/reference/generated/menu-submenu-trigger.json +++ b/docs/reference/generated/menu-submenu-trigger.json @@ -40,10 +40,6 @@ "description": "How long to wait before closing the menu that was opened on hover.\nSpecified in milliseconds.\n\nRequires the `openOnHover` prop.", "detailedType": "number | undefined" }, - "id": { - "type": "string", - "detailedType": "string | undefined" - }, "className": { "type": "string | ((state: Menu.SubmenuTrigger.State) => string | undefined)", "description": "CSS class applied to the element, or a function that\nreturns a class based on the component’s state.", diff --git a/docs/reference/generated/no-ssr.json b/docs/reference/generated/no-ssr.json deleted file mode 100644 index b8f91f3bfcb..00000000000 --- a/docs/reference/generated/no-ssr.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "NoSsr", - "description": "NoSsr purposely removes components from the subject of Server Side Rendering (SSR).\n\nThis component can be useful in a variety of situations:\n\n* Escape hatch for broken dependencies not supporting SSR.\n* Improve the time-to-first paint on the client by only rendering above the fold.\n* Reduce the rendering time on the server.\n* Under too heavy server load, you can turn on service degradation.", - "props": { - "defer": { - "type": "boolean", - "default": "false", - "description": "If `true`, the component will not only prevent server-side rendering.\nIt will also defer the rendering of the children into a different screen frame.", - "detailedType": "boolean | undefined" - }, - "fallback": { - "type": "ReactNode", - "default": "null", - "description": "The fallback content to display.", - "detailedType": "React.ReactNode" - }, - "children": { - "type": "ReactNode", - "description": "You can wrap a node.", - "detailedType": "React.ReactNode" - } - }, - "dataAttributes": {}, - "cssVariables": {} -} diff --git a/docs/reference/generated/popover-root.json b/docs/reference/generated/popover-root.json index c08384d6ce5..2780d1246f2 100644 --- a/docs/reference/generated/popover-root.json +++ b/docs/reference/generated/popover-root.json @@ -20,7 +20,7 @@ }, "actionsRef": { "type": "RefObject", - "description": "A ref to imperative actions.\n- `unmount`: When specified, the popover will not be unmounted when closed.\nInstead, the `unmount` function must be called to unmount the popover manually.\nUseful when the popover's animation is controlled by an external library.", + "description": "A ref to imperative actions.\n- `unmount`: When specified, the popover will not be unmounted when closed.\nInstead, the `unmount` function must be called to unmount the popover manually.\nUseful when the popover's animation is controlled by an external library.\n- `close`: Closes the dialog imperatively when called.", "detailedType": "| React.RefObject\n| undefined" }, "defaultTriggerId": { diff --git a/docs/reference/generated/select-root.json b/docs/reference/generated/select-root.json index 2a5e1cff129..27e64822b84 100644 --- a/docs/reference/generated/select-root.json +++ b/docs/reference/generated/select-root.json @@ -13,9 +13,9 @@ "detailedType": "Value[] | Value | null | undefined" }, "value": { - "type": "Value[] | Value", + "type": "Value[] | Value | null", "description": "The value of the select. Use when controlled.", - "detailedType": "Value[] | Value | undefined" + "detailedType": "Value[] | Value | null | undefined" }, "onValueChange": { "type": "((value: Value[] | Value | any | null, eventDetails: Select.Root.ChangeEventDetails) => void)", @@ -38,6 +38,12 @@ "description": "Event handler called when the select popup is opened or closed.", "detailedType": "| ((\n open: boolean,\n eventDetails: Select.Root.ChangeEventDetails,\n ) => void)\n| undefined" }, + "highlightItemOnHover": { + "type": "boolean", + "default": "true", + "description": "Whether moving the pointer over items should highlight them.\nDisabling this prop allows CSS `:hover` to be differentiated from the `:focus` (`data-highlighted`) state.", + "detailedType": "boolean | undefined" + }, "actionsRef": { "type": "RefObject", "description": "A ref to imperative actions.\n- `unmount`: When specified, the select will not be unmounted when closed.\nInstead, the `unmount` function must be called to unmount the select manually.\nUseful when the select's animation is controlled by an external library.", diff --git a/docs/reference/generated/switch-root.json b/docs/reference/generated/switch-root.json index ca3c5b07b84..b042f4bb77b 100644 --- a/docs/reference/generated/switch-root.json +++ b/docs/reference/generated/switch-root.json @@ -29,6 +29,11 @@ "description": "Whether the component renders a native ` ; ``` + +export const metadata = { + keywords: [ + 'Base UI Forms', + 'React Accessible Forms', + 'Field Validation Handbook', + 'React Hook Form Integration', + 'TanStack Form Guide', + 'Constraint Validation API', + 'Handbook Forms', + 'Form State', + 'Form Library', + 'Form Handling', + 'Input Validation', + 'Error Messages', + 'Form Submission', + 'Accessible Forms', + 'ARIA Forms', + ], +}; diff --git a/docs/src/app/(docs)/react/handbook/page.mdx b/docs/src/app/(docs)/react/handbook/page.mdx new file mode 100644 index 00000000000..24b7b0975cc --- /dev/null +++ b/docs/src/app/(docs)/react/handbook/page.mdx @@ -0,0 +1,157 @@ +# Handbook + +[//]: # 'This file is autogenerated, but the following list can be modified.' + +- [Styling](#styling) - [Full Docs](./styling/page.mdx) +- [Animation](#animation) - [Full Docs](./animation/page.mdx) +- [Composition](#composition) - [Full Docs](./composition/page.mdx) +- [Customization](#customization) - [Full Docs](./customization/page.mdx) +- [Forms](#forms) - [Full Docs](./forms/page.mdx) +- [TypeScript](#typescript) - [Full Docs](./typescript/page.mdx) +- [llms.txt](/llms.txt) [External] + +[//]: # 'This file is autogenerated, DO NOT EDIT AFTER THIS LINE, run: pnpm docs:validate "(docs)/react/handbook"' + +## Styling + +A guide to styling Base UI components with your preferred styling engine. + +
+ +Outline + +- Keywords: Base UI Styling, Tailwind Base UI, CSS Modules Base UI, CSS-in-JS Base UI, Style Hooks Data Attributes, Unstyled React Components, Handbook Styling, Headless UI Styling, Theming, Custom Styles, Utility First CSS, Headless Components Styling +- Sections: + - Style hooks + - CSS classes + - Data attributes + - CSS variables + - Style prop + - Tailwind CSS + - CSS Modules + - CSS-in-JS + +
+ +[Read more](./styling/page.mdx) + +## Animation + +A guide to animating Base UI components. + +
+ +Outline + +- Keywords: Base UI Animation, React Component Animations, CSS Transition Guide, Motion Framer Integration, Animation Data Attributes, Handbook Animation, Spring Animations, Enter Exit Animations, Transition Hooks, Animated Components, Keyframe Animations +- Sections: + - CSS transitions + - CSS animations + - JavaScript animations + - Elements removed from the DOM when closed + - Elements kept in the DOM when closed + - Manual unmounting + +
+ +[Read more](./animation/page.mdx) + +## Composition + +A guide to composing Base UI components with your own React components. + +
+ +Outline + +- Keywords: Base UI Composition, React Render Prop, Compose Headless Components, Custom Trigger Render, Nested Render Props, Composition Handbook, Component Composition, Render Props Pattern, Compound Components, Polymorphic Components, As Prop, Custom Elements, Wrapper Components +- Sections: + - Composing custom React components + - Composing multiple components + - Changing the default rendered element + - Render function + +
+ +[Read more](./composition/page.mdx) + +## Customization + +A guide to customizing the behavior of Base UI components. + +
+ +Outline + +- Keywords: Base UI Customization, React eventDetails, Control Base UI State, Cancel Base UI Events, Allow Propagation Tooltip, Customization Handbook, Event Handlers, Controlled Components, Uncontrolled Components, Component Behavior, Event Callbacks, State Management, Component Props, Override Behavior +- Sections: + - Base UI events + - Canceling a Base UI event + - Allowing propagation of the DOM event + - Preventing Base UI from handling a React event + - Controlling components with state + +
+ +[Read more](./customization/page.mdx) + +## Forms + +A guide to building forms with Base UI components. + +
+ +Outline + +- Keywords: Base UI Forms, React Accessible Forms, Field Validation Handbook, React Hook Form Integration, TanStack Form Guide, Constraint Validation API, Handbook Forms, Form State, Form Library, Form Handling, Input Validation, Error Messages, Form Submission, Accessible Forms, ARIA Forms +- Sections: + - Naming form controls + - Building form fields + - Submitting data + - Constraint validation + - Custom validation + - Server-side validation + - Displaying errors + - React Hook Form + - Initialize the form + - Integrate components + - Field validation + - Submitting data + - TanStack Form + - Initialize the form + - Integrate components + - Form validation + - Field validation + - Submitting data + +
+ +[Read more](./forms/page.mdx) + +## TypeScript + +A guide to using TypeScript with Base UI. + +
+ +Outline + +- Keywords: Base UI TypeScript, Component Props Namespace, Combobox ChangeEventDetails, Popover Positioner State, Toast Actions Ref Types, Use Render Component Props, Handbook TypeScript, Type Definitions, Type Safety, Generics, Component Types, Props Types, Event Types, Ref Types, Type Inference, Strongly Typed Components +- Sections: + - Namespaces + - Props + - State + - Events + - Other accessible types + +
+ +[Read more](./typescript/page.mdx) + +[//]: # 'This file is autogenerated, but the following metadata can be modified.' + +export const metadata = { + robots: { + index: false, + }, +}; diff --git a/docs/src/app/(public)/(content)/react/handbook/styling/page.mdx b/docs/src/app/(docs)/react/handbook/styling/page.mdx similarity index 87% rename from docs/src/app/(public)/(content)/react/handbook/styling/page.mdx rename to docs/src/app/(docs)/react/handbook/styling/page.mdx index 0cb585a23f0..ca078ca2535 100644 --- a/docs/src/app/(public)/(content)/react/handbook/styling/page.mdx +++ b/docs/src/app/(docs)/react/handbook/styling/page.mdx @@ -1,9 +1,9 @@ # Styling -A guide to styling Base UI components with your preferred styling engine. +A guide to styling Base UI components with your preferred styling engine. Base UI components are unstyled, don't bundle CSS, and are compatible with Tailwind, CSS Modules, CSS-in-JS, or any other styling solution you prefer. @@ -61,12 +61,12 @@ The prop also accepts a function that takes the component's state as an argument ({ color: state.checked ? 'red' : 'blue' })} /> ``` -## Tailwind CSS +## Tailwind CSS -Apply Tailwind classes to each part via the `className` prop. +Apply Tailwind CSS classes to each part via the `className` prop. ```tsx title="menu.tsx" -import { Menu } from '@base-ui-components/react/menu'; +import { Menu } from '@base-ui/react/menu'; export default function ExampleMenu() { return ( @@ -97,7 +97,7 @@ Apply custom CSS classes to each part via the `className` prop. Then style those classes in a CSS Modules file. ```tsx title="menu.tsx" -import { Menu } from '@base-ui-components/react/menu'; +import { Menu } from '@base-ui/react/menu'; import styles from './menu.module.css'; export default function ExampleMenu() { @@ -122,7 +122,7 @@ export default function ExampleMenu() { Wrap each component part and apply styles, then assemble your styled components. ```tsx title="menu.tsx" -import { Menu } from '@base-ui-components/react/menu'; +import { Menu } from '@base-ui/react/menu'; import styled from '@emotion/styled'; const StyledMenuTrigger = styled(Menu.Trigger)` @@ -157,3 +157,20 @@ const MenuExample = () => ( export default MenuExample; ``` + +export const metadata = { + keywords: [ + 'Base UI Styling', + 'Tailwind Base UI', + 'CSS Modules Base UI', + 'CSS-in-JS Base UI', + 'Style Hooks Data Attributes', + 'Unstyled React Components', + 'Handbook Styling', + 'Headless UI Styling', + 'Theming', + 'Custom Styles', + 'Utility First CSS', + 'Headless Components Styling', + ], +}; diff --git a/docs/src/app/(public)/(content)/react/handbook/typescript/page.mdx b/docs/src/app/(docs)/react/handbook/typescript/page.mdx similarity index 74% rename from docs/src/app/(public)/(content)/react/handbook/typescript/page.mdx rename to docs/src/app/(docs)/react/handbook/typescript/page.mdx index a9779d4071c..cc2849ccb52 100644 --- a/docs/src/app/(public)/(content)/react/handbook/typescript/page.mdx +++ b/docs/src/app/(docs)/react/handbook/typescript/page.mdx @@ -1,21 +1,21 @@ # TypeScript -A guide to using TypeScript with Base UI. - +A guide to using TypeScript with Base UI. + ## Namespaces -Base UI uses namespaces to organize types. Every component has two core interfaces: +Base UI uses namespaces to organize types. Every component has two core interfaces: - `Props` (such as `Tooltip.Root.Props`) - `State` (such as `Tooltip.Root.State`) ### Props -When creating wrapping components, you can use the `Props` type to accept all of the underlying Base UI props for the component. +When creating wrapping components, you can use the `Props` type to accept all of the underlying Base UI props for the component. ```tsx title="Prop types" -import { Tooltip } from '@base-ui-components/react/tooltip'; +import { Tooltip } from '@base-ui/react/tooltip'; function MyTooltip(props: Tooltip.Root.Props) { return ; @@ -47,7 +47,7 @@ function renderPositioner(props: Popover.Positioner.Props, state: Popover.Positi ### Events -Types relating to custom Base UI events are also exported on component parts' namespaces. +Types relating to custom Base UI events are also exported on component parts' namespaces. - `ChangeEventDetails` (such as `Combobox.Root.ChangeEventDetails`) is the object passed to change handlers like `onValueChange` and `onOpenChange`. - `ChangeEventReason` (such as `Combobox.Root.ChangeEventReason`) is the union of possible reason strings for a change event. @@ -70,3 +70,24 @@ The following list is non-exhaustive, and each of these are documented where nec - Popups have an `actionsRef` prop to access imperative methods. For example, `Menu.Root.Actions` gives access to the shape of the `actionsRef` object prop on ``. - The `toast` object on `` is a complex object with many properties. `Toast.Root.ToastObject` gives access to this interface. - Components that have a `render` prop have an extended `React.ComponentProps` type, enhanced with a `render` prop. The `useRender.ComponentProps` type on the function gives access to this interface. + +export const metadata = { + keywords: [ + 'Base UI TypeScript', + 'Component Props Namespace', + 'Combobox ChangeEventDetails', + 'Popover Positioner State', + 'Toast Actions Ref Types', + 'Use Render Component Props', + 'Handbook TypeScript', + 'Type Definitions', + 'Type Safety', + 'Generics', + 'Component Types', + 'Props Types', + 'Event Types', + 'Ref Types', + 'Type Inference', + 'Strongly Typed Components', + ], +}; diff --git a/docs/src/app/(public)/(content)/react/overview/about/page.mdx b/docs/src/app/(docs)/react/overview/about/page.mdx similarity index 86% rename from docs/src/app/(public)/(content)/react/overview/about/page.mdx rename to docs/src/app/(docs)/react/overview/about/page.mdx index 87c41c23e01..fccb55c45ea 100644 --- a/docs/src/app/(public)/(content)/react/overview/about/page.mdx +++ b/docs/src/app/(docs)/react/overview/about/page.mdx @@ -44,6 +44,10 @@ This means the features we use have been supported across major browsers for at For the full list of supported browsers, refer to our [.browserslistrc](https://github.com/mui/base-ui/blob/master/.browserslistrc). +## React versions + +Base UI supports React 17 and newer versions. + ## Community ### GitHub @@ -62,3 +66,21 @@ The best way to stay up-to-date on new releases and announcements is by followin ### Bluesky We're also on [Bluesky](https://bsky.app/profile/base-ui.com). + +export const metadata = { + keywords: [ + 'About Base UI', + 'Base UI Team', + 'Headless React Components', + 'Accessible React Library', + 'Open Source UI Components', + 'Base UI Community', + 'Base UI Overview', + 'Unstyled Components', + 'Component Library', + 'Headless UI Library', + 'React UI Toolkit', + 'MUI Team', + 'Radix Alternative', + ], +}; diff --git a/docs/src/app/(public)/(content)/react/overview/accessibility/page.mdx b/docs/src/app/(docs)/react/overview/accessibility/page.mdx similarity index 72% rename from docs/src/app/(public)/(content)/react/overview/accessibility/page.mdx rename to docs/src/app/(docs)/react/overview/accessibility/page.mdx index ed54f38087b..7a4124203b8 100644 --- a/docs/src/app/(public)/(content)/react/overview/accessibility/page.mdx +++ b/docs/src/app/(docs)/react/overview/accessibility/page.mdx @@ -1,16 +1,16 @@ # Accessibility -Learn how to make the most of Base UI's accessibility features and guidelines. +Learn how to make the most of Base UI's accessibility features and guidelines. Accessibility is a top priority for Base UI. Base UI components handle many complex accessibility details including ARIA attributes, role attributes, pointer interactions, keyboard navigation, and focus management. The goal is to provide an accessible user experience out of the box, with intuitive APIs for configuration. -This page highlights some of the key accessibility features of Base UI, as well as some ways you will need to augment the library, in order to ensure that your application is accessible to everyone. +This page highlights some of the key accessibility features of Base UI, as well as some ways you need to augment the library, to ensure that your application is accessible to everyone. ## Keyboard navigation @@ -37,9 +37,27 @@ Unless your application has strict requirements around compliance with current s Base UI provides components like Form, Input, Field, Fieldset to automatically associate form controls. Additionally, you can use the native HTML `
{rows.map((row) => (
{row.label} - - + {renderDialog && } + {renderMenu && } + {renderPopover && } + {renderTooltip && }
))} @@ -120,32 +75,12 @@ function Benchmark() { ); } -interface RowMenuProps { - rowData: RowData; -} - -function RowMenu({ rowData }: RowMenuProps) { +function RowMenu({ rowData }: RowProps) { return ( - - } - > - ••• - - - - - - - - Actions menu for {rowData.label} - - - - + + Menu + @@ -168,17 +103,19 @@ function RowMenu({ rowData }: RowMenuProps) { ); } -function RowPopover({ rowData }: RowMenuProps) { +function RowPopover({ rowData }: RowProps) { return ( - info + + Popover + - {rowData &&
Details for {rowData.label}
} + {rowData &&
Popover for {rowData.label}
}
@@ -186,6 +123,48 @@ function RowPopover({ rowData }: RowMenuProps) { ); } +function RowTooltip({ rowData }: RowProps) { + return ( + + + Tooltip + + + + + + + + Tooltip for {rowData.label} + + + + + ); +} + +function RowDialog({ rowData }: RowProps) { + return ( + + + Dialog + + + + + Dialog + + Dialog content for {rowData.label} + +
+ Close +
+
+
+
+ ); +} + function ArrowSvg(props: React.ComponentProps<'svg'>) { return ( @@ -204,3 +183,26 @@ function ArrowSvg(props: React.ComponentProps<'svg'>) { ); } + +export const settingsMetadata: SettingsMetadata = { + renderDialog: { + type: 'boolean', + default: true, + label: 'Render Dialog', + }, + renderMenu: { + type: 'boolean', + default: true, + label: 'Render Menu', + }, + renderPopover: { + type: 'boolean', + default: true, + label: 'Render Popover', + }, + renderTooltip: { + type: 'boolean', + default: true, + label: 'Render Tooltip', + }, +}; diff --git a/docs/src/app/(private)/experiments/perf/detached-triggers.tsx b/docs/src/app/(private)/experiments/perf/detached-triggers.tsx index aa2bf7aae14..a5bd7e3cc18 100644 --- a/docs/src/app/(private)/experiments/perf/detached-triggers.tsx +++ b/docs/src/app/(private)/experiments/perf/detached-triggers.tsx @@ -1,19 +1,31 @@ -/* eslint-disable @typescript-eslint/no-loop-func */ 'use client'; import * as React from 'react'; -import * as ReactDOM from 'react-dom'; -import { Menu } from '@base-ui-components/react/menu'; -import { Tooltip } from '@base-ui-components/react/tooltip'; -import { Popover } from '@base-ui-components/react/popover'; -import menuDemoStyles from 'docs/src/app/(public)/(content)/react/components/menu/demos/submenu/css-modules/index.module.css'; -import tooltipDemoStyles from 'docs/src/app/(public)/(content)/react/components/tooltip/demos/hero/css-modules/index.module.css'; -import popoverDemoStyles from 'docs/src/app/(public)/(content)/react/components/popover/demos/_index.module.css'; +import { Menu } from '@base-ui/react/menu'; +import { Tooltip } from '@base-ui/react/tooltip'; +import { Popover } from '@base-ui/react/popover'; +import { Dialog } from '@base-ui/react/dialog'; +import { + SettingsMetadata, + useExperimentSettings, +} from 'docs/src/components/Experiments/SettingsPanel'; +import menuDemoStyles from 'docs/src/app/(docs)/react/components/menu/demos/submenu/css-modules/index.module.css'; +import tooltipDemoStyles from 'docs/src/app/(docs)/react/components/tooltip/demos/hero/css-modules/index.module.css'; +import popoverDemoStyles from 'docs/src/app/(docs)/react/components/popover/demos/_index.module.css'; +import dialogDemoStyles from 'docs/src/app/(docs)/react/components/dialog/demos/_index.module.css'; import styles from './perf.module.css'; +import PerformanceBenchmark from './utils/benchmark'; -type RowData = { +interface Settings { + renderDialog: boolean; + renderMenu: boolean; + renderPopover: boolean; + renderTooltip: boolean; +} + +interface RowData { label: string; index: number; -}; +} const rowCount = 500; const menuItemCount = 50; @@ -29,87 +41,24 @@ const menuItems = Array.from({ length: menuItemCount }).map((_, i) => ({ })); const rowMenuHandle = Menu.createHandle(); -const genericTooltipHandle = Tooltip.createHandle(); +const genericTooltipHandle = Tooltip.createHandle(); const rowPopoverHandle = Popover.createHandle(); - -let setShowBenchmark: React.Dispatch> = ( - _: React.SetStateAction, -) => {}; +const rowDialogHandle = Dialog.createHandle(); export default function PerfExperiment() { - const runBenchmark = (iterations = 10, warmupIterations = 5) => { - const results = [] as number[]; - - for (let i = 0; i < warmupIterations + iterations; i += 1) { - ReactDOM.flushSync(() => { - setShowBenchmark(false); - }); - const start = performance.now(); - ReactDOM.flushSync(() => { - setShowBenchmark(true); - }); - if (i < warmupIterations) { - continue; - } - const end = performance.now(); - const elapsed = end - start; - results.push(Math.round(elapsed * 10) / 10); - } - - console.log(results); - console.log( - 'Average:', - Math.round((results.reduce((a, b) => a + b, 0) / results.length) * 10) / 10, - ); - console.log( - 'Std Dev:', - (() => { - const avg = results.reduce((a, b) => a + b, 0) / results.length; - const squareDiffs = results.map((value) => { - const diff = value - avg; - return diff * diff; - }); - const avgSquareDiff = squareDiffs.reduce((a, b) => a + b, 0) / squareDiffs.length; - return +Math.sqrt(avgSquareDiff).toFixed(2); - })(), - ); - }; - return (
-

Component performance - detached triggers

-
- - - - -
- +

Initial render performance - detached triggers

+ + +
); } -function Container() { - const [showBenchmark, setShowBenchmarkLocal] = React.useState(false); - - setShowBenchmark = setShowBenchmarkLocal; - - if (!showBenchmark) { - return null; - } - - return ; -} - -function Benchmark() { +function TestComponent() { + const { settings } = useExperimentSettings(); + const { renderMenu, renderTooltip, renderPopover, renderDialog } = settings; return (
@@ -117,29 +66,55 @@ function Benchmark() {
{row.label} - - info - - } - className={menuDemoStyles.Trigger} - payload={`Actions menu for ${row.label}`} - data-id={row.index} - > - ••• - + {renderDialog && ( + + Dialog + + )} + {renderMenu && ( + + Menu + + )} + {renderPopover && ( + + Popover + + )} + + {renderTooltip && ( + + Tooltip + + )}
))}
- - - + {renderDialog && } + {renderMenu && } + {renderPopover && } + {renderTooltip && }
); } @@ -185,7 +160,7 @@ function RowPopover() { - {rowData &&
Details for {rowData.label}
} + {rowData &&
Popover for {rowData.label}
} @@ -194,18 +169,18 @@ function RowPopover() { ); } -function GenericTooltip() { +function RowTooltip() { return ( - {({ payload: content }) => - content ? ( + {({ payload: rowData }) => + rowData ? ( - {content} + Tooltip for {rowData.label} @@ -215,6 +190,29 @@ function GenericTooltip() { ); } +function RowDialog() { + return ( + + {({ payload: rowData }) => + rowData ? ( + + + + Dialog + + Dialog content for {rowData.label} + +
+ Close +
+
+
+ ) : null + } +
+ ); +} + function ArrowSvg(props: React.ComponentProps<'svg'>) { return ( @@ -233,3 +231,26 @@ function ArrowSvg(props: React.ComponentProps<'svg'>) { ); } + +export const settingsMetadata: SettingsMetadata = { + renderDialog: { + type: 'boolean', + default: true, + label: 'Render Dialog', + }, + renderMenu: { + type: 'boolean', + default: true, + label: 'Render Menu', + }, + renderPopover: { + type: 'boolean', + default: true, + label: 'Render Popover', + }, + renderTooltip: { + type: 'boolean', + default: true, + label: 'Render Tooltip', + }, +}; diff --git a/docs/src/app/(private)/experiments/perf/perf.module.css b/docs/src/app/(private)/experiments/perf/perf.module.css index 9f04379edc9..f2d3a672e98 100644 --- a/docs/src/app/(private)/experiments/perf/perf.module.css +++ b/docs/src/app/(private)/experiments/perf/perf.module.css @@ -9,13 +9,53 @@ } .row { - padding: 8px; + padding: 8px 0; display: flex; justify-content: space-between; - border-bottom: 1px solid var(--color-gray-300); + align-items: center; + border-bottom: 1px solid var(--color-gray-200); } .actions { display: flex; gap: 8px; } + +.button { + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: center; + height: 1.75rem; + padding: 0 0.375rem; + margin: 0; + outline: 0; + border: 1px solid var(--color-gray-200); + border-radius: 0.375rem; + background-color: var(--color-gray-50); + font-family: inherit; + font-size: 1rem; + font-weight: 400; + line-height: 1.5rem; + color: var(--color-gray-900); + user-select: none; + + @media (hover: hover) { + &:hover { + background-color: var(--color-gray-100); + } + } + + &:active { + background-color: var(--color-gray-100); + } + + &[data-popup-open] { + background-color: var(--color-gray-100); + } + + &:focus-visible { + outline: 2px solid var(--color-blue); + outline-offset: -1px; + } +} diff --git a/docs/src/app/(private)/experiments/perf/radix-triggers.tsx b/docs/src/app/(private)/experiments/perf/radix-triggers.tsx new file mode 100644 index 00000000000..b199ba20a30 --- /dev/null +++ b/docs/src/app/(private)/experiments/perf/radix-triggers.tsx @@ -0,0 +1,201 @@ +'use client'; +import * as React from 'react'; +import { DropdownMenu, Tooltip, Popover, Dialog } from 'radix-ui'; +import { + SettingsMetadata, + useExperimentSettings, +} from 'docs/src/components/Experiments/SettingsPanel'; +import menuDemoStyles from 'docs/src/app/(docs)/react/components/menu/demos/submenu/css-modules/index.module.css'; +import tooltipDemoStyles from 'docs/src/app/(docs)/react/components/tooltip/demos/hero/css-modules/index.module.css'; +import popoverDemoStyles from 'docs/src/app/(docs)/react/components/popover/demos/_index.module.css'; +import dialogDemoStyles from 'docs/src/app/(docs)/react/components/dialog/demos/_index.module.css'; +import styles from './perf.module.css'; +import PerformanceBenchmark from './utils/benchmark'; + +interface Settings { + renderDialog: boolean; + renderMenu: boolean; + renderPopover: boolean; + renderTooltip: boolean; +} + +interface RowData { + label: string; + index: number; +} + +interface RowProps { + rowData: RowData; +} + +const rowCount = 500; +const menuItemCount = 50; + +const rows = Array.from({ length: rowCount }).map((_, i) => ({ + label: `Row ${i + 1}`, + index: i + 1, +})); + +const menuItems = Array.from({ length: menuItemCount }).map((_, i) => ({ + label: `Menu Item ${i + 1}`, + index: i + 1, +})); + +export default function PerfExperiment() { + return ( +
+

Component performance - Radix

+ + + + + +
+ ); +} + +function TestComponent() { + const { settings } = useExperimentSettings(); + const { renderMenu, renderTooltip, renderPopover, renderDialog } = settings; + return ( +
+ {rows.map((row) => ( +
+ {row.label} + + {renderDialog && } + {renderMenu && } + {renderPopover && } + {renderTooltip && } + +
+ ))} +
+ ); +} + +function RowMenu({ rowData }: RowProps) { + return ( + + + Menu + + + + + + + {menuItems.map((item) => ( + console.log(`Clicked ${item.label} for ${rowData.label}`)} + className={menuDemoStyles.Item} + > + {item.label} for {rowData.label} + + ))} + + + + ); +} + +function RowPopover({ rowData }: RowProps) { + return ( + + + Popover + + + + + + + {rowData &&
Details for {rowData.label}
} +
+
+
+ ); +} + +function RowTooltip({ rowData }: RowProps) { + return ( + + + Tooltip + + + + + + + Tooltip for {rowData.label} + + + + ); +} + +function RowDialog({ rowData }: RowProps) { + return ( + + + Dialog + + + + + Dialog + + Dialog content for {rowData.label} + +
+ Close +
+
+
+
+ ); +} + +function ArrowSvg(props: React.ComponentProps<'svg'>) { + return ( + + + + + + ); +} + +export const settingsMetadata: SettingsMetadata = { + renderDialog: { + type: 'boolean', + default: true, + label: 'Render Dialog', + }, + renderMenu: { + type: 'boolean', + default: true, + label: 'Render Menu', + }, + renderPopover: { + type: 'boolean', + default: true, + label: 'Render Popover', + }, + renderTooltip: { + type: 'boolean', + default: true, + label: 'Render Tooltip', + }, +}; diff --git a/docs/src/app/(private)/experiments/perf/utils/benchmark.tsx b/docs/src/app/(private)/experiments/perf/utils/benchmark.tsx new file mode 100644 index 00000000000..3c08dfcef66 --- /dev/null +++ b/docs/src/app/(private)/experiments/perf/utils/benchmark.tsx @@ -0,0 +1,185 @@ +'use client'; +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { useStableCallback } from '@base-ui/utils/useStableCallback'; +import { useTimeout } from '@base-ui/utils/useTimeout'; +import styles from '../perf.module.css'; + +const DOM_SETTLE_QUIET_WINDOW_MS = 32; + +const Controls = React.memo(function Controls(props: { + setShowBenchmark: React.Dispatch>; + benchmarkRootRef: React.RefObject; +}) { + const { setShowBenchmark, benchmarkRootRef } = props; + const settleTimeout = useTimeout(); + const [isRunning, setIsRunning] = React.useState(false); + const [shouldRemoveOutliers, setShouldRemoveOutliers] = React.useState(true); + + const measureDomSettled = useStableCallback(() => { + const start = performance.now(); + let lastMutationAt = start; + let observer: MutationObserver | null = null; + let resolved = false; + + return new Promise((resolve) => { + const finish = () => { + if (resolved) { + return; + } + resolved = true; + observer?.disconnect(); + settleTimeout.clear(); + resolve(Math.max(0, lastMutationAt - start)); + }; + + const root = benchmarkRootRef.current; + + if (root) { + observer = new MutationObserver(() => { + lastMutationAt = performance.now(); + settleTimeout.start(DOM_SETTLE_QUIET_WINDOW_MS, finish); + }); + + observer.observe(root, { + attributes: true, + childList: true, + characterData: true, + subtree: true, + }); + + // Resolve once the DOM stays quiet for a small window after the last mutation. + settleTimeout.start(DOM_SETTLE_QUIET_WINDOW_MS, finish); + } else { + finish(); + } + + ReactDOM.flushSync(() => { + setShowBenchmark(true); + }); + }); + }); + + const runBenchmark = useStableCallback(async (iterations: number, warmupIterations: number) => { + if (isRunning) { + console.warn('Benchmark is already running.'); + return; + } + + setIsRunning(true); + console.log(`Running benchmark: ${iterations} iterations (+${warmupIterations} warmup)...`); + try { + const results = [] as number[]; + + for (let i = 0; i < warmupIterations + iterations; i += 1) { + ReactDOM.flushSync(() => { + setShowBenchmark(false); + }); + + // eslint-disable-next-line no-await-in-loop + const domSettleDuration = await measureDomSettled(); + + if (i < warmupIterations) { + continue; + } + + results.push(Math.round(domSettleDuration * 10) / 10); + } + + logResults(shouldRemoveOutliers ? removeOutliers(results) : results); + } finally { + setIsRunning(false); + } + }); + + return ( +
+ + + + + +
+ ); +}); + +export default function PerformanceBenchmark(props: React.PropsWithChildren<{}>) { + const [showBenchmark, setShowBenchmark] = React.useState(false); + const benchmarkRootRef = React.useRef(null); + + return ( +
+ +
{showBenchmark && props.children}
+
+ ); +} + +function logResults(results: number[]) { + console.log(results); + console.log( + 'Average:', + Math.round((results.reduce((a, b) => a + b, 0) / results.length) * 10) / 10, + ); + console.log( + 'Std Dev:', + (() => { + const avg = results.reduce((a, b) => a + b, 0) / results.length; + const squareDiffs = results.map((value) => { + const diff = value - avg; + return diff * diff; + }); + const avgSquareDiff = squareDiffs.reduce((a, b) => a + b, 0) / squareDiffs.length; + return +Math.sqrt(avgSquareDiff).toFixed(2); + })(), + ); +} + +function removeOutliers(data: number[]) { + const sortedData = data.slice().sort((a, b) => a - b); + const q1Index = Math.floor(sortedData.length / 4); + const q3Index = Math.floor((sortedData.length * 3) / 4); + const q1 = sortedData[q1Index]; + const q3 = sortedData[q3Index]; + const iqr = q3 - q1; + const lowerBound = q1 - 1.5 * iqr; + const upperBound = q3 + 1.5 * iqr; + + return data.filter((value) => value >= lowerBound && value <= upperBound); +} diff --git a/docs/src/app/(private)/experiments/popover/calendar-shared.ts b/docs/src/app/(private)/experiments/popover/calendar-shared.ts index f69b79d5192..926681db9ff 100644 --- a/docs/src/app/(private)/experiments/popover/calendar-shared.ts +++ b/docs/src/app/(private)/experiments/popover/calendar-shared.ts @@ -1,4 +1,4 @@ -import { Popover } from '@base-ui-components/react/popover'; +import { Popover } from '@base-ui/react/popover'; export const eventPopover = Popover.createHandle(); diff --git a/docs/src/app/(private)/experiments/popover/calendar.tsx b/docs/src/app/(private)/experiments/popover/calendar.tsx index 28bf163897e..45cad66d6d9 100644 --- a/docs/src/app/(private)/experiments/popover/calendar.tsx +++ b/docs/src/app/(private)/experiments/popover/calendar.tsx @@ -1,6 +1,6 @@ 'use client'; import * as React from 'react'; -import { Popover } from '@base-ui-components/react'; +import { Popover } from '@base-ui/react'; import { Ellipsis, X } from 'lucide-react'; import { eventPopover, type EventData } from './calendar-shared'; import styles from './calendar.module.css'; diff --git a/docs/src/app/(private)/experiments/popover/dynamic-size.tsx b/docs/src/app/(private)/experiments/popover/dynamic-size.tsx new file mode 100644 index 00000000000..cc6a22207c2 --- /dev/null +++ b/docs/src/app/(private)/experiments/popover/dynamic-size.tsx @@ -0,0 +1,86 @@ +'use client'; +import * as React from 'react'; +import { Popover } from '@base-ui/react/popover'; +import styles from 'docs/src/app/(docs)/react/components/popover/demos/detached-triggers-full/css-modules/index.module.css'; + +const demoPopover = Popover.createHandle(); + +export default function Experiment() { + const [payload, setPayload] = React.useState(5); + + return ( +
+ + + + + + + + + + {({ payload: paragraphCount }) => ( + + + + + + + + +

Popover

+
+ + +
+ {Array.from({ length: paragraphCount ?? 0 }, (_, i) => i + 1).map((num) => ( +

Item {num}

+ ))} +
+
+
+
+ )} +
+
+ ); +} + +function ArrowSvg(props: React.ComponentProps<'svg'>) { + return ( + + + + + + ); +} + +function BellIcon(props: React.ComponentProps<'svg'>) { + return ( + + + + ); +} diff --git a/docs/src/app/(private)/experiments/popover/popovers.tsx b/docs/src/app/(private)/experiments/popover/popovers.tsx index 46d686406e3..17eb47a840c 100644 --- a/docs/src/app/(private)/experiments/popover/popovers.tsx +++ b/docs/src/app/(private)/experiments/popover/popovers.tsx @@ -1,11 +1,11 @@ 'use client'; import * as React from 'react'; -import { Popover } from '@base-ui-components/react/popover'; +import { Popover } from '@base-ui/react/popover'; import { SettingsMetadata, useExperimentSettings, } from 'docs/src/components/Experiments/SettingsPanel'; -import demoStyles from 'docs/src/app/(public)/(content)/react/components/popover/demos/detached-triggers-full/css-modules/index.module.css'; +import demoStyles from 'docs/src/app/(docs)/react/components/popover/demos/detached-triggers-full/css-modules/index.module.css'; import styles from './popovers.module.css'; const popover1 = Popover.createHandle(); @@ -68,15 +68,15 @@ export default function Popovers() {

Popovers

Uncontrolled, single trigger

- + {renderPopoverContent(0, settings)} - + {renderPopoverContent(1, settings)} - + {renderPopoverContent(2, settings)} @@ -87,6 +87,7 @@ export default function Popovers() { setSingleTriggerOpen(nextOpen)} + modal={settings.modal} > {renderPopoverContent(0, settings)} @@ -98,7 +99,7 @@ export default function Popovers() {

Uncontrolled, multiple triggers within Root

- + {({ payload }) => ( @@ -118,6 +119,7 @@ export default function Popovers() { setControlledWithinRootOpen(open); setControlledWithinRootTriggerId(eventDetails.trigger?.id ?? null); }} + modal={settings.modal} triggerId={controlledWithinRootTriggerId} > {({ payload }) => ( @@ -218,7 +220,13 @@ function StyledPopover(props: StyledPopoverProps) { const { settings } = useExperimentSettings(); return ( - + {({ payload }) => renderPopoverContent(payload, settings)} ); diff --git a/docs/src/app/(private)/experiments/popover/vertical-shared.ts b/docs/src/app/(private)/experiments/popover/vertical-shared.ts index c50360103fd..98ec67e1d6e 100644 --- a/docs/src/app/(private)/experiments/popover/vertical-shared.ts +++ b/docs/src/app/(private)/experiments/popover/vertical-shared.ts @@ -1,3 +1,3 @@ -import { Popover } from '@base-ui-components/react/popover'; +import { Popover } from '@base-ui/react/popover'; export const demoPopover = Popover.createHandle(); diff --git a/docs/src/app/(private)/experiments/popover/vertical.tsx b/docs/src/app/(private)/experiments/popover/vertical.tsx index 66a5c27fbd9..467fb9c5c03 100644 --- a/docs/src/app/(private)/experiments/popover/vertical.tsx +++ b/docs/src/app/(private)/experiments/popover/vertical.tsx @@ -1,7 +1,7 @@ 'use client'; import * as React from 'react'; -import { Popover } from '@base-ui-components/react/popover'; -import { Avatar } from '@base-ui-components/react/avatar'; +import { Popover } from '@base-ui/react/popover'; +import { Avatar } from '@base-ui/react/avatar'; import { demoPopover } from './vertical-shared'; import styles from './vertical.module.css'; diff --git a/docs/src/app/(private)/experiments/popups/popups-in-popups.tsx b/docs/src/app/(private)/experiments/popups/popups-in-popups.tsx index 8a4ec9ccb73..1be34b304b3 100644 --- a/docs/src/app/(private)/experiments/popups/popups-in-popups.tsx +++ b/docs/src/app/(private)/experiments/popups/popups-in-popups.tsx @@ -1,11 +1,11 @@ 'use client'; import * as React from 'react'; -import { Combobox } from '@base-ui-components/react/combobox'; -import { Select } from '@base-ui-components/react/select'; -import { Menu } from '@base-ui-components/react/menu'; -import { Dialog } from '@base-ui-components/react/dialog'; -import { Tooltip } from '@base-ui-components/react/tooltip'; +import { Combobox } from '@base-ui/react/combobox'; +import { Select } from '@base-ui/react/select'; +import { Menu } from '@base-ui/react/menu'; +import { Dialog } from '@base-ui/react/dialog'; +import { Tooltip } from '@base-ui/react/tooltip'; import styles from './popups-in-popups.module.css'; export default function PopupsInPopups() { diff --git a/docs/src/app/(private)/experiments/popups/popups-transform-origin.tsx b/docs/src/app/(private)/experiments/popups/popups-transform-origin.tsx index 8f6b0628eef..cb87a16350a 100644 --- a/docs/src/app/(private)/experiments/popups/popups-transform-origin.tsx +++ b/docs/src/app/(private)/experiments/popups/popups-transform-origin.tsx @@ -1,4 +1,4 @@ -import { Popover as PopoverPrimitive } from '@base-ui-components/react/popover'; +import { Popover as PopoverPrimitive } from '@base-ui/react/popover'; import styles from './popups-transform-origin.module.css'; import type { Side } from '../../../../../../packages/react/src/utils/useAnchorPositioning'; diff --git a/docs/src/app/(private)/experiments/rtl.tsx b/docs/src/app/(private)/experiments/rtl.tsx index ed96b1d7a94..a1c3d4c82d9 100644 --- a/docs/src/app/(private)/experiments/rtl.tsx +++ b/docs/src/app/(private)/experiments/rtl.tsx @@ -1,9 +1,9 @@ 'use client'; import clsx from 'clsx'; -import { DirectionProvider, useDirection } from '@base-ui-components/react/direction-provider'; -import { Menu } from '@base-ui-components/react/menu'; -import { Popover } from '@base-ui-components/react/popover'; -import { PreviewCard } from '@base-ui-components/react/preview-card'; +import { DirectionProvider, useDirection } from '@base-ui/react/direction-provider'; +import { Menu } from '@base-ui/react/menu'; +import { Popover } from '@base-ui/react/popover'; +import { PreviewCard } from '@base-ui/react/preview-card'; import c from './rtl.module.css'; const dir = 'rtl'; @@ -142,7 +142,7 @@ export default function RtlNestedMenu() { style={{ borderRadius: '50%' }} />

Base UI

-

Unstyled React components and hooks (@base-ui-components/react), by @MUI_hq.

+

Unstyled React components and hooks (@base-ui/react), by @MUI_hq.

1 Following diff --git a/docs/src/app/(private)/experiments/scroll-area/inside-menu.tsx b/docs/src/app/(private)/experiments/scroll-area/inside-menu.tsx index cf0f0783280..181a848869b 100644 --- a/docs/src/app/(private)/experiments/scroll-area/inside-menu.tsx +++ b/docs/src/app/(private)/experiments/scroll-area/inside-menu.tsx @@ -1,8 +1,8 @@ 'use client'; import * as React from 'react'; -import { Menu } from '@base-ui-components/react/menu'; -import { ScrollArea } from '@base-ui-components/react/scroll-area'; +import { Menu } from '@base-ui/react/menu'; +import { ScrollArea } from '@base-ui/react/scroll-area'; import styles from './inside-menu.module.css'; export default function ExampleMenu() { diff --git a/docs/src/app/(private)/experiments/scroll-area/inside-select.tsx b/docs/src/app/(private)/experiments/scroll-area/inside-select.tsx index 57be3bb8aa1..3cdc8b0aae5 100644 --- a/docs/src/app/(private)/experiments/scroll-area/inside-select.tsx +++ b/docs/src/app/(private)/experiments/scroll-area/inside-select.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import { Select } from '@base-ui-components/react/select'; -import { ScrollArea } from '@base-ui-components/react/scroll-area'; +import { Select } from '@base-ui/react/select'; +import { ScrollArea } from '@base-ui/react/scroll-area'; import styles from './inside-select.module.css'; export default function ExampleSelect() { diff --git a/docs/src/app/(private)/experiments/scroll-area/scroll-area-inset.tsx b/docs/src/app/(private)/experiments/scroll-area/scroll-area-inset.tsx index f097b8faccf..b30ffe7ab87 100644 --- a/docs/src/app/(private)/experiments/scroll-area/scroll-area-inset.tsx +++ b/docs/src/app/(private)/experiments/scroll-area/scroll-area-inset.tsx @@ -1,5 +1,5 @@ -import { ScrollArea } from '@base-ui-components/react/scroll-area'; -import { DirectionProvider } from '@base-ui-components/react/direction-provider'; +import { ScrollArea } from '@base-ui/react/scroll-area'; +import { DirectionProvider } from '@base-ui/react/direction-provider'; import styles from './scroll-area-inset.module.css'; export default function ScrollAreaInset() { diff --git a/docs/src/app/(private)/experiments/scroll-area/scroll-area-slight.tsx b/docs/src/app/(private)/experiments/scroll-area/scroll-area-slight.tsx index 32513f42c9d..a7ccd603c0c 100644 --- a/docs/src/app/(private)/experiments/scroll-area/scroll-area-slight.tsx +++ b/docs/src/app/(private)/experiments/scroll-area/scroll-area-slight.tsx @@ -1,4 +1,4 @@ -import { ScrollArea } from '@base-ui-components/react/scroll-area'; +import { ScrollArea } from '@base-ui/react/scroll-area'; import styles from './scroll-area-slight.module.css'; export default function ScrollAreaSlight() { diff --git a/docs/src/app/(private)/experiments/scroll-area/scroll-area.tsx b/docs/src/app/(private)/experiments/scroll-area/scroll-area.tsx index 22e9b4d32a7..7020db07c64 100644 --- a/docs/src/app/(private)/experiments/scroll-area/scroll-area.tsx +++ b/docs/src/app/(private)/experiments/scroll-area/scroll-area.tsx @@ -1,5 +1,5 @@ 'use client'; -import { ScrollArea } from '@base-ui-components/react/scroll-area'; +import { ScrollArea } from '@base-ui/react/scroll-area'; import styles from './scroll-area.module.css'; export default function ScrollAreaIntroduction() { diff --git a/docs/src/app/(private)/experiments/scroll-lock.tsx b/docs/src/app/(private)/experiments/scroll-lock.tsx index 0e51405417a..c3758aa2be3 100644 --- a/docs/src/app/(private)/experiments/scroll-lock.tsx +++ b/docs/src/app/(private)/experiments/scroll-lock.tsx @@ -1,6 +1,6 @@ 'use client'; import * as React from 'react'; -import { useScrollLock } from '@base-ui-components/utils/useScrollLock'; +import { useScrollLock } from '@base-ui/utils/useScrollLock'; export default function ScrollLock() { const [enabled, setEnabled] = React.useState(false); diff --git a/docs/src/app/(private)/experiments/select-perf.tsx b/docs/src/app/(private)/experiments/select-perf.tsx index b2838434e33..96bd2d7ec53 100644 --- a/docs/src/app/(private)/experiments/select-perf.tsx +++ b/docs/src/app/(private)/experiments/select-perf.tsx @@ -1,6 +1,6 @@ 'use client'; import * as React from 'react'; -import { Select } from '@base-ui-components/react/select'; +import { Select } from '@base-ui/react/select'; import styles from './select-perf.module.css'; const items = Array.from({ length: 1000 }, (_, i) => `Item ${i + 1}`); diff --git a/docs/src/app/(private)/experiments/slider/inset.tsx b/docs/src/app/(private)/experiments/slider/inset.tsx index f7497ce2040..49b65e128fb 100644 --- a/docs/src/app/(private)/experiments/slider/inset.tsx +++ b/docs/src/app/(private)/experiments/slider/inset.tsx @@ -1,7 +1,7 @@ 'use client'; import * as React from 'react'; import clsx from 'clsx'; -import { Slider } from '@base-ui-components/react/slider'; +import { Slider } from '@base-ui/react/slider'; import '../../../../demo-theme.css'; function InsetSlider(props: Slider.Root.Props) { diff --git a/docs/src/app/(private)/experiments/slider/slider.tsx b/docs/src/app/(private)/experiments/slider/slider.tsx index fde8b92353b..a87aaf4ee03 100644 --- a/docs/src/app/(private)/experiments/slider/slider.tsx +++ b/docs/src/app/(private)/experiments/slider/slider.tsx @@ -1,6 +1,6 @@ 'use client'; -import { Slider } from '@base-ui-components/react/slider'; -import { DirectionProvider } from '@base-ui-components/react/direction-provider'; +import { Slider } from '@base-ui/react/slider'; +import { DirectionProvider } from '@base-ui/react/direction-provider'; import { SettingsMetadata, useExperimentSettings, diff --git a/docs/src/app/(private)/experiments/storeWithControlledValues.tsx b/docs/src/app/(private)/experiments/storeWithControlledValues.tsx index 4a498acca50..76b9d5980e8 100644 --- a/docs/src/app/(private)/experiments/storeWithControlledValues.tsx +++ b/docs/src/app/(private)/experiments/storeWithControlledValues.tsx @@ -1,8 +1,8 @@ 'use client'; import * as React from 'react'; -import { useRefWithInit } from '@base-ui-components/utils/useRefWithInit'; -import { ReactStore } from '@base-ui-components/utils/store'; -import { useStableCallback } from '@base-ui-components/utils/useStableCallback'; +import { useRefWithInit } from '@base-ui/utils/useRefWithInit'; +import { ReactStore } from '@base-ui/utils/store'; +import { useStableCallback } from '@base-ui/utils/useStableCallback'; export default function Playground() { const [open, setOpen] = React.useState(false); diff --git a/docs/src/app/(private)/experiments/tabs-overflow.tsx b/docs/src/app/(private)/experiments/tabs-overflow.tsx index 0942120bfa1..4d2949d7966 100644 --- a/docs/src/app/(private)/experiments/tabs-overflow.tsx +++ b/docs/src/app/(private)/experiments/tabs-overflow.tsx @@ -1,6 +1,6 @@ 'use client'; import * as React from 'react'; -import { Tabs } from '@base-ui-components/react/tabs'; +import { Tabs } from '@base-ui/react/tabs'; import classes from './tabs-overflow.module.css'; const OVERFLOW_LABELS = Array.from({ length: 20 }, (_, index) => `Tab ${index + 1}`); diff --git a/docs/src/app/(private)/experiments/tabs.tsx b/docs/src/app/(private)/experiments/tabs.tsx index 40693b01537..48ed781cf3f 100644 --- a/docs/src/app/(private)/experiments/tabs.tsx +++ b/docs/src/app/(private)/experiments/tabs.tsx @@ -1,8 +1,8 @@ 'use client'; import * as React from 'react'; import clsx from 'clsx'; -import { DirectionProvider } from '@base-ui-components/react/direction-provider'; -import { Tabs } from '@base-ui-components/react/tabs'; +import { DirectionProvider } from '@base-ui/react/direction-provider'; +import { Tabs } from '@base-ui/react/tabs'; import { SettingsMetadata, useExperimentSettings, diff --git a/docs/src/app/(private)/experiments/toast.tsx b/docs/src/app/(private)/experiments/toast.tsx index 476fbb2951c..b9ef6fd7971 100644 --- a/docs/src/app/(private)/experiments/toast.tsx +++ b/docs/src/app/(private)/experiments/toast.tsx @@ -1,5 +1,5 @@ 'use client'; -import { Toast } from '@base-ui-components/react/toast'; +import { Toast } from '@base-ui/react/toast'; import styles from './toast.module.css'; const globalToastManager = Toast.createToastManager(); diff --git a/docs/src/app/(private)/experiments/toggle-group.tsx b/docs/src/app/(private)/experiments/toggle-group.tsx index 4616812712f..3ad029c2ddf 100644 --- a/docs/src/app/(private)/experiments/toggle-group.tsx +++ b/docs/src/app/(private)/experiments/toggle-group.tsx @@ -1,7 +1,7 @@ 'use client'; import * as React from 'react'; -import { ToggleGroup } from '@base-ui-components/react/toggle-group'; -import { Toggle } from '@base-ui-components/react/toggle'; +import { ToggleGroup } from '@base-ui/react/toggle-group'; +import { Toggle } from '@base-ui/react/toggle'; import classes from './toggle.module.css'; export default function ToggleGroupDemo() { diff --git a/docs/src/app/(private)/experiments/toolbar/basic.tsx b/docs/src/app/(private)/experiments/toolbar/basic.tsx index 5221fca25ec..ee6ed7cc76b 100644 --- a/docs/src/app/(private)/experiments/toolbar/basic.tsx +++ b/docs/src/app/(private)/experiments/toolbar/basic.tsx @@ -1,9 +1,9 @@ 'use client'; import * as React from 'react'; -import { Toolbar } from '@base-ui-components/react/toolbar'; -import { DirectionProvider } from '@base-ui-components/react/direction-provider'; +import { Toolbar } from '@base-ui/react/toolbar'; +import { DirectionProvider } from '@base-ui/react/direction-provider'; import toolbarClasses from './toolbar.module.css'; -import inputClasses from '../../../(public)/(content)/react/components/input/demos/hero/css-modules/index.module.css'; +import inputClasses from '../../../(docs)/react/components/input/demos/hero/css-modules/index.module.css'; import '../../../../demo-theme.css'; import { diff --git a/docs/src/app/(private)/experiments/toolbar/text-editor.tsx b/docs/src/app/(private)/experiments/toolbar/text-editor.tsx index 0f6b0b450e9..7b946b43b19 100644 --- a/docs/src/app/(private)/experiments/toolbar/text-editor.tsx +++ b/docs/src/app/(private)/experiments/toolbar/text-editor.tsx @@ -1,21 +1,21 @@ 'use client'; import * as React from 'react'; -import { Toolbar } from '@base-ui-components/react/toolbar'; -import { Tooltip } from '@base-ui-components/react/tooltip'; -import { Toggle } from '@base-ui-components/react/toggle'; -import { ToggleGroup } from '@base-ui-components/react/toggle-group'; -import { Select } from '@base-ui-components/react/select'; -import { NumberField } from '@base-ui-components/react/number-field'; -import { Menu } from '@base-ui-components/react/menu'; +import { Toolbar } from '@base-ui/react/toolbar'; +import { Tooltip } from '@base-ui/react/tooltip'; +import { Toggle } from '@base-ui/react/toggle'; +import { ToggleGroup } from '@base-ui/react/toggle-group'; +import { Select } from '@base-ui/react/select'; +import { NumberField } from '@base-ui/react/number-field'; +import { Menu } from '@base-ui/react/menu'; import { SettingsMetadata, useExperimentSettings, } from '../../../../components/Experiments/SettingsPanel'; import toolbarClasses from './toolbar.module.css'; -import selectClasses from '../../../(public)/(content)/react/components/select/demos/hero/css-modules/index.module.css'; -import tooltipClasses from '../../../(public)/(content)/react/components/tooltip/demos/hero/css-modules/index.module.css'; -import menuClasses from '../../../(public)/(content)/react/components/menu/demos/submenu/css-modules/index.module.css'; -import numberFieldClasses from '../../../(public)/(content)/react/components/number-field/demos/hero/css-modules/index.module.css'; +import selectClasses from '../../../(docs)/react/components/select/demos/hero/css-modules/index.module.css'; +import tooltipClasses from '../../../(docs)/react/components/tooltip/demos/hero/css-modules/index.module.css'; +import menuClasses from '../../../(docs)/react/components/menu/demos/submenu/css-modules/index.module.css'; +import numberFieldClasses from '../../../(docs)/react/components/number-field/demos/hero/css-modules/index.module.css'; import '../../../../demo-theme.css'; import { BoldIcon, diff --git a/docs/src/app/(private)/experiments/toolbar/triggers.tsx b/docs/src/app/(private)/experiments/toolbar/triggers.tsx index ccafe9ea856..aa58ec60011 100644 --- a/docs/src/app/(private)/experiments/toolbar/triggers.tsx +++ b/docs/src/app/(private)/experiments/toolbar/triggers.tsx @@ -1,21 +1,21 @@ 'use client'; import * as React from 'react'; -import { Toolbar } from '@base-ui-components/react/toolbar'; -import { Switch } from '@base-ui-components/react/switch'; -import { NumberField } from '@base-ui-components/react/number-field'; -import { Slider } from '@base-ui-components/react/slider'; -import { Tooltip } from '@base-ui-components/react/tooltip'; -import { Popover } from '@base-ui-components/react/popover'; -import { Dialog } from '@base-ui-components/react/dialog'; -import { AlertDialog } from '@base-ui-components/react/alert-dialog'; -import { Menu } from '@base-ui-components/react/menu'; +import { Toolbar } from '@base-ui/react/toolbar'; +import { Switch } from '@base-ui/react/switch'; +import { NumberField } from '@base-ui/react/number-field'; +import { Slider } from '@base-ui/react/slider'; +import { Tooltip } from '@base-ui/react/tooltip'; +import { Popover } from '@base-ui/react/popover'; +import { Dialog } from '@base-ui/react/dialog'; +import { AlertDialog } from '@base-ui/react/alert-dialog'; +import { Menu } from '@base-ui/react/menu'; import toolbarClasses from './toolbar.module.css'; import triggerToolbarClasses from './triggers.module.css'; -import menuClasses from '../../../(public)/(content)/react/components/menu/demos/submenu/css-modules/index.module.css'; -import tooltipClasses from '../../../(public)/(content)/react/components/tooltip/demos/hero/css-modules/index.module.css'; -import switchClasses from '../../../(public)/(content)/react/components/switch/demos/hero/css-modules/index.module.css'; -import dialogClasses from '../../../(public)/(content)/react/components/alert-dialog/demos/hero/css-modules/index.module.css'; -import popoverClasses from '../../../(public)/(content)/react/components/popover/demos/_index.module.css'; +import menuClasses from '../../../(docs)/react/components/menu/demos/submenu/css-modules/index.module.css'; +import tooltipClasses from '../../../(docs)/react/components/tooltip/demos/hero/css-modules/index.module.css'; +import switchClasses from '../../../(docs)/react/components/switch/demos/hero/css-modules/index.module.css'; +import dialogClasses from '../../../(docs)/react/components/alert-dialog/demos/hero/css-modules/index.module.css'; +import popoverClasses from '../../../(docs)/react/components/popover/demos/_index.module.css'; import comboSliderClasses from './slider.module.css'; import { SlidersIcon, diff --git a/docs/src/app/(private)/experiments/tooltip/prevent-open.tsx b/docs/src/app/(private)/experiments/tooltip/prevent-open.tsx index 81cce941cb0..4f4d5b2a84d 100644 --- a/docs/src/app/(private)/experiments/tooltip/prevent-open.tsx +++ b/docs/src/app/(private)/experiments/tooltip/prevent-open.tsx @@ -1,7 +1,7 @@ /* eslint-disable jsx-a11y/no-static-element-interactions */ 'use client'; import * as React from 'react'; -import { Tooltip } from '@base-ui-components/react/tooltip'; +import { Tooltip } from '@base-ui/react/tooltip'; export default function ExampleTooltip() { return ( diff --git a/docs/src/app/(private)/experiments/tooltip/tooltips.tsx b/docs/src/app/(private)/experiments/tooltip/tooltips.tsx index 250e0c8b36b..64f79fcaa27 100644 --- a/docs/src/app/(private)/experiments/tooltip/tooltips.tsx +++ b/docs/src/app/(private)/experiments/tooltip/tooltips.tsx @@ -1,12 +1,12 @@ 'use client'; import * as React from 'react'; -import { Tooltip } from '@base-ui-components/react/tooltip'; -import { StoreInspector } from '@base-ui-components/utils/store'; +import { Tooltip } from '@base-ui/react/tooltip'; +import { StoreInspector } from '@base-ui/utils/store'; import { SettingsMetadata, useExperimentSettings, } from 'docs/src/components/Experiments/SettingsPanel'; -import demoStyles from 'docs/src/app/(public)/(content)/react/components/tooltip/demos/detached-triggers-full/css-modules/index.module.css'; +import demoStyles from 'docs/src/app/(docs)/react/components/tooltip/demos/detached-triggers-full/css-modules/index.module.css'; import styles from './tooltips.module.css'; const tooltip1Handle = Tooltip.createHandle(); diff --git a/docs/src/app/(private)/experiments/tooltip/transitions.tsx b/docs/src/app/(private)/experiments/tooltip/transitions.tsx index f4b3ecf7276..7a3b434ea5f 100644 --- a/docs/src/app/(private)/experiments/tooltip/transitions.tsx +++ b/docs/src/app/(private)/experiments/tooltip/transitions.tsx @@ -1,6 +1,6 @@ 'use client'; import * as React from 'react'; -import { Tooltip } from '@base-ui-components/react/tooltip'; +import { Tooltip } from '@base-ui/react/tooltip'; import { motion, AnimatePresence } from 'motion/react'; import styles from './transitions.module.css'; diff --git a/docs/src/app/(private)/layout.tsx b/docs/src/app/(private)/layout.tsx index 50f871668b7..32df34c1634 100644 --- a/docs/src/app/(private)/layout.tsx +++ b/docs/src/app/(private)/layout.tsx @@ -1,7 +1,87 @@ import * as React from 'react'; +import { Metadata, Viewport } from 'next'; import 'docs/src/styles.css'; import './layout.css'; export default function Layout({ children }: React.PropsWithChildren) { - return children; + return ( + // Use suppressHydrationWarning to avoid https://github.com/facebook/react/issues/24430 + + + + + + + {children} + + ); } + +export const viewport: Viewport = { + initialScale: 1, + width: 'device-width', +}; + +export const metadata: Metadata = { + title: { + template: '%s · Base UI', + default: 'Base UI', + }, + twitter: { + site: '@base_ui', + card: 'summary_large_image', + }, + openGraph: { + type: 'website', + locale: 'en_US', + title: { + template: '%s · Base UI', + default: 'Base UI', + }, + ttl: 604800, + }, + icons: { + icon: [ + { + rel: 'icon', + url: + process.env.NODE_ENV !== 'production' ? '/static/favicon-dev.ico' : '/static/favicon.ico', + sizes: '32x32', + }, + { + rel: 'icon', + type: 'image/svg+xml', + url: + process.env.NODE_ENV !== 'production' ? '/static/favicon-dev.svg' : '/static/favicon.svg', + }, + ], + apple: [ + { + rel: 'apple-touch-icon', + url: '/static/apple-touch-icon.png', + }, + ], + }, + robots: { + index: false, + follow: false, + }, +}; diff --git a/docs/src/app/(private)/not-found.tsx b/docs/src/app/(private)/not-found.tsx new file mode 100644 index 00000000000..7dbea8c3ab0 --- /dev/null +++ b/docs/src/app/(private)/not-found.tsx @@ -0,0 +1,5 @@ +import { NotFound } from 'docs/src/components/NotFound'; + +export default function NotFoundPage() { + return ; +} diff --git a/docs/src/app/(public)/(content)/layout.css b/docs/src/app/(public)/(content)/layout.css deleted file mode 100644 index dc094b5d507..00000000000 --- a/docs/src/app/(public)/(content)/layout.css +++ /dev/null @@ -1,50 +0,0 @@ -@import 'docs/src/breakpoints.css'; - -@layer components { - .ContentLayoutRoot { - --sidebar-width: 17.5rem; - - display: grid; - align-items: start; - padding-top: var(--header-height); - padding-inline: 1.5rem; - grid-template-columns: 1fr; - - @media (--sm) { - padding-inline: 2.5rem; - } - - @media (--show-side-nav) { - padding-top: 0; - padding-inline: 0; - grid-template-columns: var(--sidebar-width) 1fr 3rem; - } - - @media (--show-quick-nav) { - grid-template-columns: var(--sidebar-width) 1fr var(--sidebar-width); - } - } - - .ContentLayoutMain { - min-width: 0; - max-width: 48rem; - width: 100%; - - padding-top: 1.5rem; - padding-bottom: 5rem; - margin: 0 auto; - - @media (--sm) { - padding-top: 2rem; - } - - @media (--show-side-nav) { - margin: 0; - padding-bottom: 8rem; - } - - @media (--show-quick-nav) { - margin: 0; - } - } -} diff --git a/docs/src/app/(public)/(content)/layout.tsx b/docs/src/app/(public)/(content)/layout.tsx deleted file mode 100644 index 63e2281ccf5..00000000000 --- a/docs/src/app/(public)/(content)/layout.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import * as React from 'react'; -import type { Metadata } from 'next/types'; -import * as SideNav from 'docs/src/components/SideNav'; -import * as QuickNav from 'docs/src/components/QuickNav/QuickNav'; -import { Header } from 'docs/src/components/Header'; -import { MAIN_CONTENT_ID } from 'docs/src/components/SkipNav'; -import { nav } from 'docs/src/nav'; -import './layout.css'; - -export default function Layout({ children }: React.PropsWithChildren) { - return ( -
-
- - {nav.map((section) => ( - - {section.label} - - {section.links.map((link) => ( - - {link.label} - {link.isNew && New} - - ))} - - - ))} - - -
- {children} -
-
- ); -} - -// Title and description are pulled from

and in the MDX. -export const metadata: Metadata = { - title: null, - description: null, -}; diff --git a/docs/src/app/(public)/(content)/test/quick-nav/page.mdx b/docs/src/app/(public)/(content)/test/quick-nav/page.mdx deleted file mode 100644 index 0cbd8ad8cb0..00000000000 --- a/docs/src/app/(public)/(content)/test/quick-nav/page.mdx +++ /dev/null @@ -1,247 +0,0 @@ -# Quick nav test page - -Just testing the quick nav overflow. - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent erat leo, rutrum vitae nunc ac, venenatis condimentum nulla. - -Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Duis vitae sollicitudin sem, ac fermentum dui. Vivamus commodo, urna a cursus vehicula, nisl enim sollicitudin arcu, ultricies ultrices massa odio a justo. Aenean facilisis nisl quis eleifend dictum. Curabitur efficitur neque quis enim posuere placerat. - -Pellentesque consectetur, libero a feugiat pretium, nulla nunc lacinia sem, id pharetra ipsum diam eget enim. Morbi bibendum quis magna nec blandit. Suspendisse in dignissim sapien. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ut finibus nisl, a gravida lectus. Praesent placerat tempor ultrices. Mauris fringilla non nisl at congue. - -## foo - -Pellentesque sed eros eu sem euismod venenatis. Sed iaculis mauris vitae erat maximus ultricies. Donec vel interdum arcu. Etiam nec risus sollicitudin, scelerisque neque eu, auctor nibh. Ut condimentum tempor augue aliquet fringilla. Sed sodales blandit elit, quis tincidunt nisl consequat et. Donec lacinia libero nec nibh bibendum consectetur. Duis porttitor venenatis consectetur. Quisque consectetur pulvinar tellus. - -Morbi quis dictum tellus. Duis tincidunt lorem sem. Nullam hendrerit sollicitudin nulla. Donec arcu eros, suscipit in gravida blandit, imperdiet vitae est. Vestibulum at ipsum finibus, aliquam dolor ac, hendrerit ante. Fusce odio sem, vestibulum at tempor quis, pharetra id sem. Nunc diam nisi, volutpat sit amet suscipit at, eleifend sed velit. Fusce egestas, nisi vel fermentum sagittis, augue leo viverra erat, et semper turpis risus eu erat. Pellentesque sit amet ante a tortor molestie tincidunt. Etiam condimentum, tellus in congue euismod, dui massa sodales tortor, vitae molestie dui ipsum eget elit. Duis non ligula cursus, vestibulum est et, blandit orci. Aliquam nec erat faucibus, gravida turpis vitae, ultricies mi. - -## bar - -Morbi porta nibh at aliquam venenatis. Suspendisse vitae odio purus. Ut id sollicitudin mauris. Fusce rutrum rhoncus sodales. Sed tempor mi vitae vestibulum hendrerit. Suspendisse congue eros sit amet libero pharetra congue. Phasellus tempus sapien felis, et viverra quam laoreet non. Maecenas et ex gravida, ullamcorper mauris id, imperdiet metus. Donec eget maximus urna. Quisque maximus libero felis, vitae consequat leo viverra ac. Praesent sit amet metus gravida, consectetur nunc vitae, vulputate nisi. Sed dignissim elit id ante scelerisque molestie. Vivamus faucibus leo vel nibh pharetra, quis posuere risus rutrum. Vivamus bibendum at massa et posuere. Nam euismod nunc eget tellus ultricies rutrum. Proin imperdiet dui ornare feugiat gravida. - -Phasellus in tincidunt orci. Morbi quis odio eu metus finibus dignissim id ac nulla. Mauris commodo nibh elit, eget suscipit nunc ornare a. In faucibus ullamcorper imperdiet. Cras lacus tellus, bibendum eget odio eu, auctor accumsan quam. Morbi tempus, tellus egestas lacinia gravida, tellus felis fermentum lacus, ac elementum orci turpis vel turpis. Sed sed nisi vestibulum, facilisis ex sed, tristique mi. - -## baz - -Nam ante neque, convallis eu rutrum at, bibendum ut augue. Curabitur consectetur lorem purus, eu varius turpis ornare vitae. Maecenas vitae sapien mollis eros laoreet condimentum sed eu sem. Nulla nec elementum metus. Ut ac arcu at leo bibendum vulputate eu a lacus. Vestibulum pulvinar tortor lobortis dapibus sollicitudin. Praesent lacinia laoreet eros, sit amet ornare neque malesuada at. Phasellus eu tristique tellus. Nunc justo quam, venenatis id lectus eget, sodales molestie lorem. Curabitur mollis vel ex a pharetra. Mauris bibendum felis cursus, auctor diam sit amet, gravida ligula. Nam mollis urna leo. Quisque consequat at neque eu auctor. Ut risus lectus, egestas non egestas nec, commodo nec quam. Pellentesque id nulla vel turpis pharetra placerat vel blandit mi. - -Vestibulum volutpat metus et arcu eleifend congue. In ac tortor commodo, gravida nisi in, tempor turpis. Sed aliquet tempor mi, quis ullamcorper ipsum accumsan sit amet. Ut id ipsum a ante venenatis placerat. Aliquam tempor porta sapien sed euismod. Pellentesque id maximus neque, eget venenatis erat. Maecenas sodales, turpis vel rutrum dignissim, risus massa iaculis mi, id dictum quam orci id lacus. Sed molestie velit vitae neque pulvinar hendrerit. Nam vitae placerat elit. Praesent rutrum neque tortor, id accumsan ante feugiat ut. - -## qux - -Etiam ac mauris aliquam, maximus augue at, rutrum purus. Aenean feugiat ante vel neque eleifend, ut vulputate mi pharetra. Quisque ac commodo augue, a lobortis dolor. Mauris id consequat est, at consectetur ex. Nam ut odio sit amet ipsum pharetra consequat vel sit amet metus. Quisque erat ligula, rhoncus at porttitor et, dictum vel turpis. Ut egestas tincidunt sem, ut pulvinar lectus mollis vel. Suspendisse euismod, nisl cursus gravida imperdiet, massa metus rutrum augue, et dictum dolor dolor luctus leo. In ornare viverra sollicitudin. Proin lacinia orci vitae mauris pharetra maximus. Proin vitae felis nibh. Vestibulum dapibus, sapien et eleifend auctor, ipsum magna hendrerit lectus, id aliquam lorem urna sed ligula. Nam ullamcorper leo malesuada convallis finibus. Nunc ac varius ipsum. Nullam volutpat purus mauris, sit amet dignissim ex tempus et. - -Donec maximus ex laoreet purus sagittis, a ornare mi finibus. In non placerat lectus. Mauris nibh sem, accumsan a dui ac, vulputate sollicitudin enim. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In ac ipsum dolor. Nunc at lorem magna. Phasellus elementum tellus diam, suscipit sagittis ligula viverra ac. Morbi a nibh et turpis lobortis lobortis eget vitae diam. - -Fusce et porttitor orci, quis consectetur tortor. Vestibulum vitae ullamcorper tortor. Integer facilisis felis id tempus fringilla. Nulla vitae egestas nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; In pharetra mauris nec dolor lacinia molestie. Vestibulum at ornare nibh. - -## quux - -Phasellus facilisis elementum est eu tempus. Donec ac urna quis libero bibendum euismod ac vel lacus. Ut feugiat nisl non lorem eleifend sagittis. In non ligula a nisi efficitur semper. Curabitur semper purus et metus commodo mattis. Etiam sollicitudin nibh in nisl mollis, ultrices molestie ligula ultrices. Pellentesque sit amet malesuada est. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Ut a diam elementum, fringilla nisi sit amet, sagittis purus. Vivamus non eleifend orci. Nullam a scelerisque ligula, rhoncus fringilla dolor. Aliquam id hendrerit est, sit amet posuere risus. - -Sed et justo risus. Fusce facilisis ultricies quam non vestibulum. Donec vulputate ac purus id bibendum. Pellentesque sollicitudin nec ligula at auctor. Donec in lorem interdum, consequat magna vel, faucibus mi. Duis felis massa, pharetra non tincidunt at, porttitor ac turpis. Morbi in nisl et risus venenatis aliquam. Mauris interdum porta purus, sit amet faucibus erat pretium non. - -Curabitur gravida vulputate ullamcorper. Vivamus euismod metus ante, at ornare lorem condimentum a. Etiam a lorem sollicitudin, feugiat augue sit amet, viverra magna. Phasellus consectetur, elit id laoreet condimentum, ipsum lacus ultrices elit, in posuere enim enim vel diam. Pellentesque fermentum est non nisl gravida molestie. In fermentum bibendum imperdiet. Donec congue purus sollicitudin est egestas, nec facilisis sem finibus. Aenean hendrerit mi et sagittis tincidunt. Nunc auctor, sapien a bibendum dapibus, augue dolor efficitur risus, at dapibus ante lorem sit amet tellus. Suspendisse enim nisi, sodales sed elementum a, elementum quis mauris. In congue iaculis mauris a tristique. Curabitur viverra tincidunt justo ut tempor. - -In nec varius eros, sit amet auctor ex. Proin porttitor quam sed dui tristique, vel luctus dolor elementum. Proin maximus, neque eu maximus porttitor, ligula tellus ultrices orci, a aliquam nunc tortor et diam. Sed interdum egestas efficitur. Pellentesque ut lacinia sapien. Etiam a accumsan dui, sit amet maximus neque. Etiam accumsan faucibus sapien, eu rutrum purus luctus in. Nam augue ligula, accumsan ut suscipit eu, blandit vitae orci. Donec a erat leo. Mauris porta eros vel elit hendrerit, et tincidunt justo facilisis. Aenean maximus arcu nec elit pellentesque, ac aliquam tortor faucibus. Morbi ac congue purus, in mollis felis. Curabitur pulvinar at lorem ut dictum. Vestibulum vitae ex ullamcorper, vulputate lacus at, dictum massa. Ut vitae nibh a lorem vehicula mollis eget non sapien. - -## corge - -Maecenas euismod augue et elit interdum, vel semper massa fermentum. Donec vel pretium dui. Etiam ultrices velit vel arcu malesuada fermentum. Aenean nulla nunc, pretium non aliquet in, lobortis hendrerit sapien. Curabitur ac ullamcorper nunc. Mauris accumsan, diam eu dictum suscipit, metus ligula gravida orci, vitae posuere leo sem vitae diam. Quisque in purus sed justo viverra hendrerit vitae hendrerit tortor. - -## grault - -Ut sit amet hendrerit leo. Nunc finibus augue et vestibulum venenatis. Curabitur metus velit, convallis vitae nibh nec, euismod suscipit tellus. Cras blandit odio sed nunc pretium, a dictum orci laoreet. Suspendisse vulputate, sapien quis rhoncus suscipit, felis turpis faucibus erat, a ullamcorper dolor tortor at risus. Fusce congue sollicitudin augue. Morbi diam sapien, feugiat quis viverra eget, efficitur ut nunc. Vestibulum velit diam, consequat et leo quis, fermentum semper lectus. Aenean in augue suscipit, maximus urna id, interdum sem. Fusce eu nisi nec mauris maximus dignissim eget ut quam. Proin eu ipsum malesuada, sagittis leo viverra, vulputate nibh. Mauris fringilla tortor quis fringilla placerat. Nam quam ante, dictum nec volutpat non, finibus rhoncus libero. - -Sed in fringilla orci, eget vehicula odio. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin nec metus vitae est varius lacinia. Duis et est augue. Nullam mollis est tellus, quis faucibus eros venenatis quis. Ut eu ipsum porta, consequat nisl ac, consequat nulla. Donec sed tincidunt nibh. Phasellus sed porta magna. Integer vel lorem turpis. - -## garply - -Pellentesque quis tincidunt velit. Morbi congue nulla in dolor fermentum mollis et ac mauris. Aliquam tincidunt, est sed feugiat congue, tellus sem rhoncus ligula, et ultrices massa neque ut risus. Vestibulum tellus erat, ornare at ultrices vel, lacinia nec velit. Suspendisse condimentum purus sit amet lorem accumsan, et aliquet velit molestie. Nam interdum ultrices justo. Donec sed augue id lacus varius vulputate. - -Donec gravida mauris et est aliquet malesuada. In viverra auctor ante. Duis nec lobortis erat, sit amet aliquet mi. In augue turpis, fringilla nec nisi eget, iaculis pharetra ex. Quisque finibus augue eget enim tempor, a pharetra magna mollis. Nulla id erat erat. Donec ante eros, lobortis a velit quis, ultrices sollicitudin ligula. Suspendisse potenti. Pellentesque commodo mollis velit quis pretium. - -Nullam sed justo eu urna tempor auctor sit amet ut est. Aliquam condimentum dolor sit amet laoreet mollis. Donec vehicula ut velit ut sagittis. Quisque quis tortor nec lorem congue varius. Quisque convallis mi in dolor gravida interdum. Ut dapibus nisi eget orci volutpat dignissim. Duis maximus, arcu non laoreet viverra, eros elit commodo elit, ut tempor ante nunc eget ex. Vivamus vulputate porttitor cursus. - -## waldo - -Nulla mollis facilisis sem et facilisis. Donec molestie mattis orci nec consectetur. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Fusce nulla nulla, eleifend vitae lacus in, dapibus auctor lacus. Morbi ex lacus, bibendum id purus ac, tempor dictum justo. Sed vitae lorem tortor. Sed vel eros porttitor massa euismod congue nec ut diam. Praesent blandit nulla sed mollis finibus. Curabitur tincidunt ipsum sed imperdiet molestie. Fusce at aliquet diam. - -Proin vel dapibus diam, fermentum semper mi. Cras at augue suscipit justo lacinia tristique. Phasellus ac tempor purus. Integer ac turpis eu neque vehicula feugiat. Phasellus a imperdiet massa, nec condimentum purus. Aliquam fringilla sollicitudin venenatis. Nullam viverra ultrices sapien vel mattis. Curabitur porttitor ornare scelerisque. Phasellus id nunc sed ligula rutrum pharetra sed eu sem. Nullam malesuada lacus non dolor dignissim, et accumsan justo vestibulum. Aenean at est cursus, tincidunt dui id, fringilla felis. Nunc dictum convallis nibh, ac semper odio pulvinar sit amet. Nunc ornare condimentum sem, a tincidunt erat hendrerit a. Vestibulum a ex dignissim, lobortis augue eget, scelerisque massa. Nam at nisl non neque pharetra efficitur a in nisl. - -Sed erat erat, tristique id porta nec, dapibus et lorem. Praesent tincidunt sem orci, sed volutpat erat facilisis sed. Nam libero libero, maximus a sapien non, elementum ornare lectus. Aliquam ac mauris nisl. Sed semper blandit nisl, at finibus ante consequat a. Cras a urna quis risus venenatis sodales. Nulla velit quam, porttitor sed risus in, pharetra pulvinar risus. Nulla cursus nisi vel volutpat placerat. Proin interdum rutrum dui ac imperdiet. - -Vivamus semper felis eu suscipit tempus. Praesent justo tellus, dapibus sit amet commodo quis, luctus sit amet lectus. Nunc suscipit convallis turpis sit amet euismod. Integer convallis euismod erat, hendrerit pellentesque ligula malesuada vel. Nam lobortis posuere nibh, ac tempus elit mollis eu. Fusce neque velit, interdum quis felis id, malesuada porttitor justo. Cras non neque a justo cursus malesuada. Suspendisse vitae feugiat metus, at consequat sapien. Curabitur leo sapien, imperdiet ac lacus ac, pharetra molestie urna. Donec semper purus nec placerat tempor. Sed nec dignissim magna. - -## fred - -Aliquam dignissim, quam vitae lobortis porttitor, ligula mi dictum enim, sit amet convallis felis orci vitae turpis. Pellentesque consectetur ipsum id felis lobortis luctus. Vestibulum feugiat, leo a interdum rutrum, elit nisl varius sapien, ut dignissim ipsum lacus sed quam. Vivamus purus ipsum, tempor eu risus et, cursus varius tortor. In orci lectus, gravida nec erat vitae, pharetra tristique metus. Sed congue blandit massa, et posuere ex facilisis id. Maecenas tempus consequat velit sed venenatis. - -## plugh - -Morbi ac lectus id massa fringilla blandit. Fusce mattis, mi nec rhoncus vulputate, dolor orci faucibus velit, quis ultricies nunc est posuere odio. Integer bibendum nibh suscipit dolor euismod pellentesque. Curabitur blandit volutpat viverra. Curabitur sollicitudin sollicitudin magna, quis varius dui volutpat quis. Curabitur neque lorem, laoreet quis lobortis ac, lobortis nec purus. Proin felis odio, auctor id magna at, aliquam pharetra tellus. Donec eget metus lorem. Pellentesque sem sem, dapibus non mi eget, ornare pulvinar ligula. - -## xyzzy - -Nunc finibus ac est in aliquam. Nulla vitae tincidunt ex. Nam vitae eros cursus, rhoncus eros a, dictum dolor. Aenean et pharetra velit. Mauris ante metus, dapibus vel commodo nec, ultrices vel tellus. Morbi non augue rhoncus, sagittis orci non, ultrices augue. Vestibulum enim sem, fermentum vitae fermentum eu, gravida blandit turpis. Mauris ullamcorper nec lacus ac iaculis. Donec in erat pretium, euismod augue eget, dictum magna. Integer sed nunc mauris. Praesent libero odio, molestie id est nec, feugiat suscipit diam. - -Integer posuere diam ut tortor sodales, ac varius sapien sodales. Curabitur aliquet sem sit amet augue sagittis, nec posuere nulla sollicitudin. Aenean cursus augue enim, nec rhoncus est varius in. In ultricies sapien nec mauris malesuada malesuada. Donec non nibh sapien. Sed dui nisl, congue vitae purus in, efficitur fringilla magna. Mauris eleifend nulla nec dolor iaculis, ut convallis felis elementum. Pellentesque accumsan bibendum nisl eu placerat. Mauris laoreet ex nec arcu maximus rhoncus. Suspendisse sed fringilla arcu. Donec tristique eros tortor, sit amet tempus nulla dictum nec. Integer justo urna, molestie eu felis ultricies, volutpat tempor mi. - -## thud - -Nulla rutrum ultrices tellus in dignissim. Donec id sem dapibus, malesuada diam a, mollis justo. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aliquam nulla est, tempor eget interdum in, posuere condimentum quam. Pellentesque commodo porta neque eget euismod. Pellentesque auctor nulla non consequat volutpat. Vivamus tincidunt pulvinar felis, id efficitur massa facilisis nec. Aenean at cursus elit. Donec luctus interdum urna ut ultrices. - -## wibble - -Nulla malesuada mauris eu gravida aliquet. Suspendisse luctus nunc mauris, sit amet faucibus urna suscipit in. Aenean congue risus in fringilla cursus. Donec eu dui tempor, dictum lectus vel, bibendum orci. Curabitur at elit posuere, pellentesque turpis vel, eleifend sapien. Nam placerat sem id libero consequat molestie. Vivamus augue justo, vehicula nec felis ut, vulputate sodales diam. Nulla in commodo eros. Sed aliquet, elit sed dapibus fringilla, purus urna feugiat tortor, elementum ultricies ex nunc ac ante. Cras a venenatis nulla. Nulla vel dolor dignissim, bibendum diam at, tincidunt nulla. Nulla quis mi a tortor cursus tincidunt nec at leo. Nam gravida ac lorem ut feugiat. Fusce vestibulum tristique neque a ultrices. Curabitur auctor ultrices tincidunt. Ut sit amet cursus purus, et varius ex. - -## wobble - -Cras tempus interdum libero, vitae tempor risus scelerisque sed. Sed in hendrerit massa, a tempus ex. Sed ac dolor ut mauris feugiat finibus. Cras eu lacus sit amet odio tincidunt fermentum. Duis ac interdum arcu. Cras ut purus at ligula aliquet gravida. Duis quis pharetra leo. In fermentum dolor ut nisl ornare dignissim. Maecenas porta, velit nec vulputate pharetra, lectus diam maximus massa, at euismod est ex iaculis justo. Nam aliquet condimentum dui, sed fringilla ligula aliquam sed. Phasellus vel pharetra magna, at eleifend neque. Phasellus et imperdiet erat. Maecenas massa eros, euismod vel vulputate vitae, facilisis nec mauris. Sed et orci mauris. Cras nec ligula at risus sodales commodo non vel urna. Aenean et est fringilla tellus convallis rutrum. - -## flob - -In quam odio, aliquet quis orci ut, blandit venenatis magna. Integer nec bibendum libero, nec faucibus leo. Mauris tortor elit, egestas sed luctus vitae, placerat ut elit. Curabitur sit amet cursus velit. In ut nisi in nibh volutpat sodales nec eu mi. Curabitur id massa ac risus porttitor tincidunt ac vulputate diam. Nunc ultricies facilisis lacus iaculis dignissim. In vel tellus id felis eleifend tristique. Proin ac iaculis dui. - -Maecenas ac mi id dui malesuada pellentesque hendrerit et elit. Sed in sagittis nunc, sed mollis lorem. Morbi fermentum lorem sed felis accumsan, varius laoreet est imperdiet. Vestibulum porta scelerisque feugiat. Proin facilisis lacus quis tellus fringilla, a venenatis odio fermentum. Mauris placerat risus ut lectus molestie euismod. Donec bibendum sem et eros varius dignissim. In varius dui a quam porta, ac placerat ante gravida. - -## glorp - -Fusce finibus semper turpis et condimentum. Praesent quis lectus a tellus pretium sollicitudin id et dui. Nunc at nisl egestas, porttitor ligula at, faucibus ex. Maecenas tortor lorem, ultrices eu sodales ut, maximus in odio. Suspendisse tincidunt enim ut placerat porta. Fusce mollis, mauris in commodo ultrices, enim lectus pretium sapien, eget semper magna sapien vel purus. Donec ac ullamcorper diam. Integer eget lacinia justo, sit amet condimentum risus. Vestibulum lacinia mi metus. Sed pretium tellus at pulvinar tincidunt. - -## zazzle - -Sed vel ipsum nunc. Nulla orci magna, tincidunt pulvinar ligula accumsan, bibendum consectetur dolor. Fusce scelerisque vestibulum orci vestibulum eleifend. Sed nisi dui, molestie et augue eu, vulputate ornare augue. Sed convallis erat tortor, et semper neque vehicula at. Mauris eget malesuada eros. Vestibulum non rutrum mauris, vel tempus dolor. Cras at sem non nunc varius vestibulum at ac lectus. Nullam tincidunt vel leo sed efficitur. Quisque facilisis nisi vel nisl viverra tincidunt. Donec sed lacinia est. Integer sed lacus ut augue mollis mollis. - -Sed auctor quam aliquet, luctus quam ac, scelerisque dui. Aliquam tincidunt odio elit, at efficitur felis vestibulum et. Proin congue ornare pulvinar. In elementum tortor at nisi hendrerit semper. Phasellus vel arcu sem. Nam euismod in felis ac molestie. Vivamus fermentum varius nisl ac condimentum. Sed porttitor in magna ac condimentum. Sed vulputate libero rutrum elit sagittis lacinia. Curabitur sit amet finibus quam. Cras nec enim consectetur, venenatis nulla eget, porttitor arcu. - -## blorp - -Vivamus vulputate, ante at dictum convallis, dolor justo mollis libero, vel bibendum justo urna et elit. Nullam sed mollis tortor. Nam non ante ac nisl egestas posuere eget sit amet orci. Morbi iaculis scelerisque purus id euismod. Donec eget ante ligula. Praesent et interdum sem, in consequat lacus. Integer lorem felis, convallis vel tincidunt lobortis, aliquet vestibulum nunc. Fusce sem dui, convallis tempor pulvinar at, pharetra vitae ante. - -Quisque mollis tincidunt tellus, et placerat risus luctus ac. Praesent vel diam eu neque aliquam posuere. Aliquam lobortis venenatis risus, vitae mattis diam mollis molestie. Aliquam euismod, felis id viverra gravida, purus enim pellentesque dolor, vel dictum orci massa quis velit. Vivamus egestas aliquet dolor eget ultricies. Cras feugiat vitae nibh non semper. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque nec ultrices dui, sit amet semper felis. Sed ligula elit, pulvinar sit amet facilisis tincidunt, cursus vel enim. - -Sed in libero lacus. Morbi felis enim, sollicitudin ac tortor quis, sollicitudin accumsan est. Aenean venenatis dui non nibh consectetur cursus. Vivamus nec nunc auctor, malesuada nunc ac, egestas metus. Sed eget ex eu arcu luctus aliquam. Vestibulum posuere lobortis consequat. Curabitur non tortor cursus odio feugiat sodales. Suspendisse sed lectus mattis, sollicitudin diam a, ultrices tortor. Phasellus imperdiet malesuada metus, id euismod urna interdum eu. Mauris eget placerat ex. Nullam dapibus, augue eget gravida mattis, sapien elit rutrum orci, a tempor felis orci nec turpis. Duis nibh est, euismod at elementum eget, porta in tortor. Ut mollis ullamcorper fermentum. Nullam a congue erat. - -## snorf - -Aenean sapien erat, aliquam vel elit ac, feugiat sodales diam. Suspendisse potenti. Quisque eget sodales dui. Cras fermentum nec leo sed elementum. Sed eu viverra erat, sed pharetra sapien. Vivamus imperdiet purus quam, non sollicitudin quam sagittis dignissim. Donec nisi orci, lobortis vel ligula sed, egestas blandit neque. Sed est quam, varius mattis ultricies nec, accumsan et velit. Ut non dui non sem semper tristique id sit amet ante. Curabitur auctor et dolor quis viverra. Nullam maximus, turpis sit amet tempor convallis, turpis tellus volutpat arcu, sit amet fermentum est felis vel lectus. Praesent congue risus ut venenatis pharetra. Ut fringilla erat ut laoreet accumsan. Nam sit amet enim quam. Nunc at dictum erat. - -## trazz - -Nullam sit amet rutrum dui. Sed in ex vel ligula congue elementum eget a felis. Donec mattis scelerisque urna non accumsan. Quisque pharetra egestas lorem. Vestibulum posuere sapien vitae velit lacinia dapibus. Mauris porta vel purus rhoncus volutpat. Sed dapibus dictum viverra. Fusce non ipsum vel purus pharetra fermentum. In hac habitasse platea dictumst. Donec ullamcorper arcu sed consectetur finibus. Maecenas sit amet lobortis mauris. Aenean pulvinar diam nec enim pretium, vel malesuada massa feugiat. Quisque luctus at eros eget iaculis. Vivamus vitae orci blandit, facilisis mauris quis, varius arcu. Nullam tellus felis, luctus sit amet placerat id, ultrices non ante. - -Quisque et imperdiet arcu. Etiam sit amet sodales dui. Quisque tincidunt diam mi, a vestibulum mauris commodo a. Morbi bibendum ante sit amet enim convallis, nec feugiat lorem molestie. Quisque viverra libero elementum efficitur tempor. Nullam faucibus eget velit at ultricies. Cras tincidunt, purus ac vulputate gravida, sem ligula eleifend sapien, vel iaculis tellus elit at neque. Proin imperdiet sodales sapien ut ullamcorper. Nulla quis nulla ut leo bibendum vulputate convallis vitae ipsum. Nullam molestie at nulla non scelerisque. - -## blizz - -Nam sed sapien consectetur, tempus neque non, fringilla lectus. Nunc nec metus tortor. Maecenas eu ultrices sem, in feugiat turpis. Donec ac pulvinar eros. Nulla pellentesque, ex non sollicitudin gravida, felis nisi pharetra odio, ut hendrerit lectus neque in turpis. Vivamus porttitor nulla ut tellus mattis auctor. Praesent fermentum nunc erat, in commodo arcu aliquet ac. Sed tempus facilisis libero. Pellentesque tempor neque vitae sem tristique, eget iaculis arcu vestibulum. Mauris ac lacinia dui. - -## dribble - -Donec commodo turpis eget sem euismod, at scelerisque diam ullamcorper. Integer vitae varius libero, vel feugiat nisi. Nam et maximus magna. Pellentesque varius efficitur vulputate. Aenean sed feugiat sem, eu hendrerit ex. Integer pellentesque iaculis tempor. Vivamus vel ligula sit amet elit finibus varius lobortis ut justo. Mauris sodales, nunc vel molestie auctor, nisi quam fermentum tortor, id imperdiet arcu erat euismod turpis. Curabitur blandit ante a felis feugiat ultricies. - -## squark - -Praesent non leo sed nibh laoreet efficitur. Nulla facilisi. Vestibulum fermentum ligula ut mauris ullamcorper, in dapibus nibh porta. Duis convallis pellentesque ligula non ullamcorper. Vivamus eget lacus quis erat dignissim aliquam ac sed metus. Sed malesuada eget ligula ac tristique. Donec blandit et ipsum fringilla tempor. - -## flarn - -Cras non porta dui, nec posuere ante. Morbi tempor neque nunc, id ornare leo bibendum eu. Fusce eu turpis ut mauris malesuada imperdiet a sed felis. Etiam nec dapibus libero. Phasellus semper lorem id lacus sodales, sed tincidunt massa pharetra. Sed gravida nulla nec efficitur congue. Aliquam molestie egestas suscipit. - -## zizzle - -Phasellus tristique, mauris non lacinia tincidunt, risus ex sollicitudin libero, nec facilisis mi sem sed sem. Curabitur tellus nibh, vehicula sed lacus in, ultricies lobortis ligula. Phasellus sit amet massa non justo volutpat efficitur. Sed dictum urna risus, a sollicitudin nulla molestie eget. Donec sagittis quam mattis neque facilisis, sit amet pharetra lacus lacinia. Vivamus malesuada arcu felis, vel facilisis dolor efficitur non. Duis id lacinia purus, accumsan malesuada felis. Nam a leo ac erat dignissim luctus. Donec ultrices rutrum elit vel commodo. Quisque vitae diam pretium, dapibus lectus sed, congue est. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Quisque commodo malesuada urna, sed cursus diam fringilla sit amet. Morbi scelerisque turpis lorem, et euismod sapien porttitor sit amet. Sed pellentesque eros urna, vitae lobortis nibh vestibulum rutrum. Sed tristique sollicitudin pretium. Morbi accumsan velit vel nunc suscipit mattis. - -## blarn - -Nulla ullamcorper est vel leo sodales porttitor. Praesent eu pellentesque ex. Praesent non tincidunt lectus. Cras ipsum tellus, finibus vel massa quis, efficitur tincidunt neque. Aliquam convallis nisi ut nisl dictum, a pellentesque justo elementum. Integer gravida id dui ac consequat. Donec tincidunt purus nulla, sit amet scelerisque quam auctor et. Vestibulum fermentum ex nec ex gravida, ac mattis massa rutrum. - -## grizzle - -Ut viverra vitae magna sed feugiat. Proin a consequat tortor. Duis in massa neque. Cras eget placerat tortor. Maecenas tincidunt volutpat ullamcorper. Quisque id iaculis neque, vel porttitor velit. Pellentesque ac tincidunt orci, nec rhoncus urna. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Pellentesque aliquet blandit libero, at feugiat sapien efficitur in. Proin ut velit nunc. Proin a tincidunt tortor. Nam non blandit sapien. Morbi et erat mollis, tristique dui quis, vulputate risus. - -## plonk - -Cras neque sapien, lobortis ut luctus ut, tempor eu dolor. Ut placerat suscipit magna, a euismod dui imperdiet quis. Aenean fermentum a mauris et vulputate. Maecenas finibus egestas aliquet. Nulla a lectus pharetra nunc pretium interdum vitae et dui. Integer id fermentum magna, quis dictum lorem. Duis maximus rutrum odio eu iaculis. Nam feugiat metus non nulla sollicitudin, vitae tempor mauris porttitor. Vivamus accumsan nibh vitae neque tempus maximus eu sed magna. Nulla lobortis tempor dui eget elementum. Nam congue, enim sit amet sodales pretium, mi turpis varius ante, id viverra felis diam in justo. Aliquam vestibulum massa in ultrices imperdiet. In fringilla aliquam turpis a efficitur. Ut tincidunt nulla non facilisis semper. Quisque dapibus dictum ex et congue. - -## squibble - -Morbi accumsan vestibulum convallis. Duis pretium efficitur volutpat. Donec elit elit, pretium sed condimentum at, eleifend vitae neque. Sed consectetur varius semper. Praesent mollis sapien eu turpis vehicula ornare. Aenean interdum augue ex, sit amet luctus mi accumsan vitae. Quisque aliquet luctus sapien, ut venenatis ante volutpat pulvinar. Curabitur dictum nibh non ipsum fringilla, tempus mollis justo aliquam. Sed gravida vulputate consequat. Nulla odio neque, rhoncus eu scelerisque sit amet, posuere sed mi. Mauris nec justo elit. Vivamus vestibulum, ante vel iaculis pharetra, lorem lacus eleifend urna, id rutrum tellus neque eget risus. Etiam ultrices nulla mi, a luctus augue tincidunt eu. - -## snizzle - -Nullam hendrerit id risus nec accumsan. Morbi ac ligula nec sem aliquam efficitur. Vivamus eleifend, justo ut cursus lacinia, arcu magna rhoncus purus, eu pretium dolor ante nec nulla. Cras volutpat vulputate nisi, et dapibus odio vulputate in. Phasellus convallis posuere quam id egestas. Morbi non velit urna. Pellentesque semper convallis viverra. Sed rutrum libero dolor. In quis nisi volutpat, venenatis ligula ut, volutpat urna. Nulla eu ipsum eu nisl lobortis ultricies. - -## blort - -Mauris gravida quam ligula, semper condimentum mauris ornare quis. Praesent bibendum libero nec velit efficitur finibus. Nunc scelerisque turpis ultrices elit gravida, non aliquet eros pulvinar. Quisque placerat dolor in lacinia aliquet. Donec sem neque, fermentum eget felis quis, ullamcorper scelerisque nulla. Curabitur nec urna accumsan, efficitur ipsum ac, facilisis sapien. Nulla lobortis enim sit amet justo aliquam pellentesque. Vivamus aliquam blandit ante, vel pharetra lectus dignissim et. Sed porta, quam nec consequat accumsan, metus leo iaculis elit, eu imperdiet arcu sapien ut metus. Proin sit amet sem quis ligula fermentum suscipit ac nec orci. Aliquam sed elit sed arcu aliquet lobortis. Praesent sollicitudin posuere malesuada. Duis nec mauris eu nisl sodales consectetur. - -## drizz - -Pellentesque ullamcorper ipsum et urna lobortis molestie. Morbi eu porttitor justo. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In vitae metus et sem facilisis venenatis nec et diam. Etiam porta erat vitae risus feugiat, eget sollicitudin erat molestie. Nullam at diam felis. Sed ultrices nisi sed dui tempus dignissim. Phasellus mattis, est sed porta elementum, lacus ex facilisis metus, nec congue turpis tortor egestas nulla. Curabitur varius finibus enim eu semper. Aliquam erat volutpat. - -## fuzzle - -Curabitur gravida libero sed risus convallis, non dictum lectus placerat. Sed in nisl a ipsum auctor suscipit. Fusce ligula erat, condimentum eget ex eget, euismod gravida mauris. Fusce eu libero ultricies, venenatis lacus nec, blandit velit. Vestibulum et egestas sapien, ac mattis lorem. Nunc consequat vestibulum elit, bibendum faucibus purus dapibus et. Praesent ornare lacus at nibh mollis finibus. Nulla eget dolor id urna faucibus vehicula. Integer varius lacus quis metus blandit, eu semper augue consequat. Vivamus sit amet neque a lectus varius consequat. Pellentesque a tristique orci. - -## snarf - -Donec venenatis, ex a aliquet viverra, velit purus posuere augue, sed mollis erat nibh eget erat. Curabitur quis pretium lectus. Maecenas et mauris eu nisl feugiat sollicitudin in non risus. Sed consectetur quam in elit lacinia scelerisque. Cras ornare tincidunt vehicula. Vestibulum augue ex, molestie ut fringilla ac, suscipit sit amet sapien. In nec sem nisl. Vestibulum feugiat nulla metus. Vestibulum ornare ultricies lectus, id fringilla elit varius in. Donec malesuada lectus odio, sed luctus dui tempor quis. In hac habitasse platea dictumst. - -## kludge - -Donec luctus in neque a dignissim. Pellentesque vestibulum a sapien eget volutpat. Nunc ullamcorper maximus aliquet. Aliquam varius quam vehicula, hendrerit massa a, viverra eros. Etiam blandit mauris sed neque hendrerit, quis aliquet leo euismod. Vivamus convallis massa at felis dignissim, quis finibus nisl aliquam. Ut ac mauris porta urna dapibus dignissim ac ut nibh. Duis molestie nunc diam, in ullamcorper velit dapibus vitae. Mauris et hendrerit arcu. Nam vestibulum consectetur velit non ultricies. Proin consectetur arcu quam, a rutrum nunc rhoncus id. Sed ultricies sollicitudin ornare. Proin suscipit bibendum enim, eu dignissim ex consequat sed. - -## plurb - -Etiam luctus semper posuere. Duis vitae nisl accumsan, sagittis erat eu, placerat eros. Praesent ac urna id urna ultricies blandit in sed libero. Aenean neque mi, volutpat a libero a, maximus lacinia turpis. Suspendisse porttitor massa sed augue pulvinar convallis. Vivamus nec porttitor libero. Sed arcu turpis, malesuada in dui sed, faucibus fringilla mi. Phasellus rhoncus venenatis dui eget venenatis. - -## glitz - -Ut ullamcorper maximus metus, nec efficitur velit blandit non. Mauris lobortis, lorem vel hendrerit mattis, magna ipsum semper dui, in placerat ipsum velit pulvinar enim. Suspendisse id mauris a diam accumsan iaculis. Morbi a sollicitudin erat. Fusce eu porta quam, eget tempus nulla. Etiam quis elit libero. Nullam a risus laoreet, sollicitudin lacus a, dictum odio. Donec magna mi, egestas id ante quis, pellentesque accumsan massa. Donec nisl velit, aliquam id est eget, malesuada elementum quam. - -Suspendisse pharetra urna id tellus pulvinar consequat. Nunc efficitur purus ex, eget molestie urna ullamcorper ut. Curabitur sed metus elementum, ultrices orci at, ultrices tellus. Phasellus viverra varius elementum. In ut mi ac diam tincidunt dignissim sed eu est. Proin lectus ipsum, congue a sapien ac, maximus blandit odio. Praesent vitae massa in est dapibus feugiat. - -## blurp - -Vestibulum vitae viverra libero, a facilisis lacus. Donec suscipit, ligula eget molestie viverra, lectus turpis ultrices est, ut elementum leo lorem in ligula. Aliquam condimentum enim eget sem ornare maximus. Proin mollis vehicula ante, eget ornare nulla dapibus non. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus semper tincidunt egestas. Etiam ac venenatis odio. Mauris iaculis, mi vitae fermentum volutpat, velit magna rutrum lectus, vulputate commodo felis nisl eu velit. Duis laoreet nulla nunc, vel tincidunt sem scelerisque non. Proin a leo ac diam volutpat porttitor vel vitae eros. Donec et congue ipsum. Nulla quis sem tincidunt, vestibulum tellus ut, porttitor metus. Nullam nec porta justo. - -## clonk - -Curabitur id lectus nulla. Duis eu consequat tortor. Proin eget nisi sed velit porta ultrices ut sed erat. Vestibulum nec porttitor nulla. Nullam id turpis nec elit condimentum pretium ac id arcu. Sed quis lacus nec lectus porttitor aliquet. Etiam cursus elit diam, ac malesuada lectus tincidunt sed. - -## splat - -Suspendisse tempus condimentum nisl pellentesque feugiat. Suspendisse eu ultrices libero. Suspendisse rutrum feugiat dui. Integer et nunc et tortor cursus luctus. Phasellus quis convallis nulla. Nulla pellentesque blandit neque. Vivamus non nisl et libero hendrerit sagittis eget eget quam. Ut eget risus ac ligula ullamcorper ultrices. Duis mattis tellus sed dolor placerat, non condimentum ipsum semper. Suspendisse non ligula vel nulla venenatis gravida. Etiam id aliquam lacus. Maecenas lacinia libero sit amet erat tincidunt dignissim. Nam malesuada neque eu libero viverra, vitae cursus eros blandit. Proin nunc turpis, cursus non ultricies et, efficitur at augue. - -## crinkle - -Fusce quam nisl, tincidunt ut arcu non, bibendum accumsan elit. Sed eget urna lectus. Curabitur blandit nisi a ligula aliquam, nec consectetur ligula convallis. Ut eu urna sodales, vestibulum nibh vitae, suscipit dolor. Fusce rhoncus non libero nec efficitur. Pellentesque consectetur accumsan dui eu dignissim. Nam ligula elit, pulvinar id magna vel, dignissim condimentum nisi. Praesent quis nisi sagittis, ultricies erat mattis, facilisis diam. Donec volutpat massa luctus sapien aliquam, id blandit augue volutpat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Cras purus metus, venenatis quis nunc vel, dapibus convallis nisi. Pellentesque eu gravida augue, at venenatis velit. Morbi euismod finibus nisl condimentum consequat. Praesent scelerisque erat arcu, a cursus nulla dignissim ut. Ut id faucibus dolor. Praesent egestas, enim ut pulvinar rutrum, odio turpis semper sem, et volutpat tortor velit sit amet lacus. - -## spork - -Fusce et arcu arcu. Proin gravida nisl sed purus iaculis fringilla. Suspendisse tempus eget ipsum venenatis finibus. In elit metus, convallis laoreet erat eget, tincidunt commodo dui. Sed laoreet massa sit amet erat sagittis efficitur. Ut id tristique magna. Curabitur hendrerit, ex eu iaculis auctor, elit lectus posuere felis, a varius eros urna a quam. Aenean commodo interdum vestibulum. Duis lobortis mauris ut lorem efficitur malesuada. Sed condimentum congue dolor, ut imperdiet lacus iaculis sed. Phasellus in diam ac mauris dapibus gravida. Mauris eleifend eu nunc ac euismod. Morbi malesuada turpis vitae tellus ultrices varius. In hac habitasse platea dictumst. Curabitur aliquet cursus lectus, quis congue est euismod a. Donec condimentum venenatis nisl et varius. - -## flibble - -Pellentesque vulputate rutrum mauris in rutrum. Pellentesque porta nulla id nisl ultricies, in malesuada purus posuere. Integer blandit sapien eu est hendrerit euismod nec id metus. Praesent quis massa quis lorem pellentesque ultricies a et nibh. Nam tempus laoreet leo, et tristique purus consectetur quis. Nunc eget imperdiet quam. Phasellus tincidunt in nisi vel maximus. Aliquam pharetra nibh vitae posuere sagittis. Morbi non enim at augue pulvinar aliquet id quis tortor. Pellentesque facilisis sollicitudin mauris vitae ultricies. Aliquam vitae massa magna. Morbi eu nulla tempor, pharetra justo at, varius quam. Etiam at viverra ex, vel pulvinar risus. Aliquam ac tellus in sem volutpat vulputate tincidunt porta tellus. Proin vitae mauris orci. Nulla condimentum ex arcu, vel dictum felis varius quis. - -## glimmer - -Nunc bibendum efficitur erat nec molestie. Nam enim dui, congue sed placerat quis, dictum eget eros. Cras ac fringilla velit. Morbi tincidunt mauris a nunc dapibus gravida. Curabitur feugiat nisi vel elit cursus, pulvinar dignissim nisl rhoncus. Quisque dui augue, vestibulum eget molestie ut, dapibus in felis. Pellentesque bibendum lorem quis lorem pellentesque rhoncus. Mauris euismod mi sem, nec semper sem imperdiet ac. Donec pellentesque scelerisque dignissim. Vestibulum in consequat dui, ac rhoncus magna. Nunc pharetra ipsum magna, eget aliquet lorem blandit a. Etiam hendrerit venenatis ligula, ac scelerisque nisi feugiat commodo. Nulla sit amet scelerisque tellus. - -## twizzle - -Donec nec lacus sed ex commodo hendrerit. Integer lectus orci, finibus vitae ultrices ac, placerat nec justo. Pellentesque ultrices viverra rhoncus. Nam ultricies dui lacus, in venenatis neque lacinia non. Aliquam erat volutpat. Nam sit amet venenatis felis, sed porta diam. Donec condimentum dui diam, id rhoncus nibh porta sed. Ut justo ligula, euismod vitae consectetur in, euismod et felis. Sed sit amet consectetur erat. Ut fringilla eleifend faucibus. Proin eget eleifend neque. Praesent eu lobortis odio. - -## splurge - -Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec non ornare ante, ut auctor odio. Maecenas condimentum blandit nisi vulputate fermentum. Nunc elementum odio ut enim dictum placerat. Cras tortor massa, facilisis a est eu, convallis venenatis mi. In hac habitasse platea dictumst. Nunc ultricies eget arcu vitae ultricies. Fusce vitae mi dui. Nulla sit amet justo molestie mi vulputate auctor a quis neque. Ut ultrices sem ex, nec fermentum ante facilisis lacinia. Nullam id eros faucibus, tempor urna ut, ornare erat. Ut molestie, tellus vel ultricies iaculis, sem enim maximus massa, in pretium neque ipsum et odio. Pellentesque et tortor at dolor fringilla tempus. - -## blip - -Etiam luctus molestie porta. Donec vehicula eros massa, nec laoreet erat mollis id. Pellentesque tristique, dolor ac venenatis porttitor, lorem dolor lobortis ipsum, non venenatis enim justo vitae enim. Sed sollicitudin justo nec nulla volutpat, accumsan sodales diam fermentum. Donec eu diam in tellus accumsan eleifend. In vel varius lacus, quis tincidunt ex. Ut ullamcorper ligula et interdum eleifend. Fusce dui tortor, cursus sed ex at, efficitur pretium odio. Maecenas bibendum tempor massa non tempus. Donec dapibus tellus a tellus eleifend, a lacinia mi facilisis. Duis facilisis interdum sem, non congue ex. Integer congue nunc in nibh vehicula blandit. Ut viverra odio vel pharetra vehicula. Suspendisse quis dolor consectetur, eleifend eros nec, vestibulum turpis. Morbi malesuada tellus et justo rhoncus porta. - -## zap - -Suspendisse tincidunt volutpat tincidunt. Morbi ut ex eget risus tristique pellentesque. Aliquam sollicitudin sem quis elit fringilla auctor. Nunc commodo semper ullamcorper. Donec tristique eleifend felis. Aenean malesuada quis neque in congue. Duis vel elit fringilla, dignissim libero sed, tempus turpis. diff --git a/docs/src/app/(public)/careers/design-engineer/page.tsx b/docs/src/app/(public)/careers/design-engineer/page.tsx deleted file mode 100644 index 9b651f10570..00000000000 --- a/docs/src/app/(public)/careers/design-engineer/page.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import type { Metadata } from 'next'; -import { ArrowRightIcon } from 'docs/src/icons/ArrowRightIcon'; -import { Header } from 'docs/src/components/Header'; -import '../../page.css'; -import '../../(content)/layout.css'; - -export default function Homepage() { - return ( -
-
-
-

Design Engineer

-

- Help us make Base UI the most intuitive, accessible, and powerful open-source UI library - for React. -

-

About Base UI

-

- From the creators of Radix, Material UI, and Floating UI, Base UI is an unstyled React - component library for building accessible user interfaces. Our focus is on accessibility, - performance, and developer experience. Our goal is to provide a complete set of - open-source UI components, with a delightful developer experience, in a sustainable way. -

-

- The Base UI team is a small group of engineers, designers, and product people, working to - solve incredibly complex and challenging UI problems on the web. In our work, we value - craft, flexibility, and accessibility. -

-

The role

-

- We're looking for an experienced Design Engineer to join the team at Staff–Senior Staff - level. You will help us improve the library across the board, including API design, - performance, a11y, testing workflows, docs, support, and implementing components. -

-

- We're looking for someone who enjoys wearing many hats. Think less about spending many - months implementing a single component, and more about leading developer experience, API - design, a11y, docs, and maintaining overall product quality. -

-

- You will have a lot of autonomy to push for improvements. We are looking for someone who - is passionate about UI design, with an exceptional eye for detail, and great taste in both - visual design and API design. -

-

- Responsibilities -

-
    -
  • Report to our OSS Engineering Manager.
  • -
  • - Contribute to the component design process through a11y research, user research, writing - design specs, collaborating with engineers, and exploring UI design patterns on both web - and mobile. -
  • -
  • - Contribute to the API design process at both the component level and the library level. - Work to ensure APIs are intuitive, configurable, and consistent. -
  • -
  • - Lead the docs design and user experience. Design and implement new features, new - content, a11y enhancements, and visual language enhancements. -
  • -
  • Build and maintain the Base UI website.
  • -
  • - Build out our test environment by composing and styling component examples, then - rigorously testing them in many different environments including desktop, mobile, and - screen readers. -
  • -
  • - Create a healthy feedback loop with the engineering team, pushing for iterative - improvements to accessibility, usability, and performance. -
  • -
  • - Champion Base UI both internally and externally, contributing to marketing efforts - through social media, blogs, conference talks, podcasts, and other communication - channels. -
  • -
  • Help out with developer support on both Github and Discord.
  • -
-

Requirements

-
    -
  • We are targeting 7+ years of web development experience.
  • -
  • - Working knowledge of React, TypeScript, Next.js, MDX, Git, Figma, and other popular - tools in the React, JavaScript, and design ecosystems. -
  • -
  • - Expert knowledge of CSS, including familiarity with common CSS tooling, and knowledge of - bleeding-edge CSS features. -
  • -
  • - Deep familiarity with the headless UI ecosystem. Strong opinions on the pros and cons of - API design choices across headless UI libraries. -
  • -
  • - Expert knowledge of a11y, including deep familiarity with ARIA guidelines, WCAG success - criterion, and screen reader technologies. -
  • -
  • Advanced understanding of design principles.
  • -
  • - A passion for craft, a keen eye for detail, and exquisite taste. -
  • -
  • Excellent written and verbal communication skills.
  • -
  • Experience working remotely and communicating asynchronously.
  • -
-

Benefits

-
    -
  • $200k–$290k USD base salary.
  • -
  • - Subsidized healthcare package (dependent on employment path and location). -
  • -
  • - Flexible time-off. We provide 33 days of paid time-off globally. -
  • -
  • 100% remote. Our entire company is globally distributed.
  • -
  • - Company retreats. We meet up every 8 months for a week of work and fun. -
  • -
  • - Office equipment. We let you choose the hardware of your choice. -
  • -
  • - 20% development time. Allocate 20% of your time towards personal and professional - development. -
  • -
  • - Education budget. We provide mentorship and send you to events that help you build your - network and skills. -
  • -
-

- The actual salary will be determined by skill-level, experience, and location. More - information about the specific compensation package will be shared during the hiring - process. -

-

Application

-

The hiring process will consist of 6 stages:

-
    -
  1. Resume review.
  2. -
  3. 45 minute Phone screen.
  4. -
  5. React challenge (asynchronous).
  6. -
  7. 60 minute meeting with Product lead.
  8. -
  9. 90 minute meeting with the Base UI team.
  10. -
  11. 60 minute culture-fit interview.
  12. -
- - Apply Now - -
-
- ); -} - -const description = 'Unstyled UI components for building accessible web apps and design systems.'; - -export const metadata: Metadata = { - description, - twitter: { - description, - }, - openGraph: { - description, - }, -}; diff --git a/docs/src/app/(public)/layout.tsx b/docs/src/app/(public)/layout.tsx deleted file mode 100644 index 61e6db7e7ed..00000000000 --- a/docs/src/app/(public)/layout.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import * as React from 'react'; -import type { Metadata, Viewport } from 'next/types'; -import { GoogleAnalytics } from 'docs/src/components/GoogleAnalytics'; -import { DocsProviders } from 'docs/src/components/DocsProviders'; -import 'docs/src/styles.css'; -import './layout.css'; - -export default function Layout({ children }: React.PropsWithChildren) { - return ( - -
-
-
{children}
- -
-
- -
- ); -} - -export const metadata: Metadata = { - metadataBase: new URL('https://base-ui.com'), -}; - -export const viewport: Viewport = { - themeColor: [ - // Desktop Safari page background - { - media: '(prefers-color-scheme: light) and (min-width: 1024px)', - color: 'oklch(95% 0.25% 264)', - }, - { - media: '(prefers-color-scheme: dark) and (min-width: 1024px)', - color: 'oklch(25% 1% 264)', - }, - - // Mobile Safari header background (match the site header) - { - media: '(prefers-color-scheme: light)', - color: 'oklch(98% 0.25% 264)', - }, - { - media: '(prefers-color-scheme: dark)', - color: 'oklch(17% 1% 264)', - }, - ], -}; diff --git a/docs/src/app/(public)/page.css b/docs/src/app/(public)/page.css deleted file mode 100644 index 8d1fec51037..00000000000 --- a/docs/src/app/(public)/page.css +++ /dev/null @@ -1,44 +0,0 @@ -@import 'docs/src/breakpoints.css'; - -@layer base { - body { - /* iOS overscroll background */ - background-color: var(--color-content); - - @media (--lg) { - background-color: var(--color-background); - } - } -} - -@layer components { - .HomepageRoot { - display: flex; - flex-direction: column; - flex-grow: 1; - padding-top: 3rem; - padding-bottom: calc(3rem + 5vh); - padding-inline: 2rem; - align-items: center; - justify-content: center; - } - - .HomepageContent { - width: 100%; - max-width: 25rem; - text-wrap: balance; - } - - .HomepageHeading { - font-size: var(--text-xl); - font-weight: 500; - line-height: var(--text-xl--line-height); - letter-spacing: var(--text-xl--letter-spacing); - margin-bottom: 0.5rem; - } - - .HomepageCaption { - color: var(--color-gray-600); - margin-bottom: 2rem; - } -} diff --git a/docs/src/app/(public)/page.tsx b/docs/src/app/(public)/page.tsx deleted file mode 100644 index b82db81a882..00000000000 --- a/docs/src/app/(public)/page.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import * as React from 'react'; -import type { Metadata, Viewport } from 'next'; -import { Link } from 'docs/src/components/Link'; -import { ArrowRightIcon } from 'docs/src/icons/ArrowRightIcon'; -import { Logo } from 'docs/src/components/Logo'; -import './page.css'; - -export default function Homepage() { - return ( - - {/* Set the Site name for Google results. https://developers.google.com/search/docs/appearance/site-names */} -