From 0072f1698ae95585f9cc269e3bd110a6e84f384a Mon Sep 17 00:00:00 2001 From: Darius Cepulis Date: Tue, 17 Mar 2026 14:30:21 -0500 Subject: [PATCH 1/6] feat(site): migrate search from Pagefind to Algolia DocSearch v4 - Replace Pagefind integration with Algolia DocSearch v4 - Add two search indices (docs + blog) with framework faceting - Add Ask AI assistant backed by markdown index - Theme DocSearch modal and trigger to match site design system - Add pre-hydration placeholder button for client:idle loading - Add data-search-ignore to Tab layout-stability span - Add getMissingResultsUrl for missing results feedback Co-Authored-By: Claude Opus 4.6 (1M context) --- pnpm-lock.yaml | 314 ++++++++++++++- site/CLAUDE.md | 44 +-- site/README.md | 11 +- site/astro.config.mjs | 2 - site/integrations/pagefind.ts | 94 ----- site/package.json | 4 +- site/src/components/Search/Search.astro | 363 ++++++++++++++---- site/src/components/Search/Search.tsx | 118 ++---- site/src/components/Search/searchIcon.svg | 12 - site/src/components/Tabs.tsx | 4 +- site/src/components/docs/FrameworkCase.astro | 2 +- site/src/pages/blog/[...slug].astro | 11 +- .../framework/[framework]/[...slug].astro | 3 +- site/src/search.config.ts | 7 + 14 files changed, 658 insertions(+), 331 deletions(-) delete mode 100644 site/integrations/pagefind.ts delete mode 100644 site/src/components/Search/searchIcon.svg create mode 100644 site/src/search.config.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6923e9c92..807fce89c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -428,6 +428,12 @@ importers: '@base-ui/react': specifier: ^1.2.0 version: 1.2.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@docsearch/css': + specifier: ^4.6.0 + version: 4.6.0 + '@docsearch/react': + specifier: ^4.6.0 + version: 4.6.0(@algolia/client-search@5.49.2)(@types/react@19.2.14)(algoliasearch@5.49.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(search-insights@2.17.3) '@mux/mux-node': specifier: ^12.8.1 version: 12.8.1 @@ -437,9 +443,6 @@ importers: '@nanostores/react': specifier: ^1.0.0 version: 1.0.0(nanostores@1.1.1)(react@19.2.4) - '@pagefind/default-ui': - specifier: ^1.4.0 - version: 1.4.0 '@sentry/astro': specifier: ^10.32.1 version: 10.42.0(astro@5.18.0(@netlify/blobs@10.7.0)(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.59.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(rollup@4.59.0) @@ -567,9 +570,6 @@ importers: prettier-plugin-astro: specifier: ^0.14.1 version: 0.14.1 - sirv: - specifier: ^3.0.2 - version: 3.0.2 tsx: specifier: ^4.20.3 version: 4.21.0 @@ -615,6 +615,76 @@ packages: '@adobe/css-tools@4.4.4': resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} + '@algolia/abtesting@1.15.2': + resolution: {integrity: sha512-rF7vRVE61E0QORw8e2NNdnttcl3jmFMWS9B4hhdga12COe+lMa26bQLfcBn/Nbp9/AF/8gXdaRCPsVns3CnjsA==} + engines: {node: '>= 14.0.0'} + + '@algolia/autocomplete-core@1.19.2': + resolution: {integrity: sha512-mKv7RyuAzXvwmq+0XRK8HqZXt9iZ5Kkm2huLjgn5JoCPtDy+oh9yxUMfDDaVCw0oyzZ1isdJBc7l9nuCyyR7Nw==} + + '@algolia/autocomplete-plugin-algolia-insights@1.19.2': + resolution: {integrity: sha512-TjxbcC/r4vwmnZaPwrHtkXNeqvlpdyR+oR9Wi2XyfORkiGkLTVhX2j+O9SaCCINbKoDfc+c2PB8NjfOnz7+oKg==} + peerDependencies: + search-insights: '>= 1 < 3' + + '@algolia/autocomplete-shared@1.19.2': + resolution: {integrity: sha512-jEazxZTVD2nLrC+wYlVHQgpBoBB5KPStrJxLzsIFl6Kqd1AlG9sIAGl39V5tECLpIQzB3Qa2T6ZPJ1ChkwMK/w==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + + '@algolia/client-abtesting@5.49.2': + resolution: {integrity: sha512-XyvKCm0RRmovMI/ChaAVjTwpZhXdbgt3iZofK914HeEHLqD1MUFFVLz7M0+Ou7F56UkHXwRbpHwb9xBDNopprQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-analytics@5.49.2': + resolution: {integrity: sha512-jq/3qvtmj3NijZlhq7A1B0Cl41GfaBpjJxcwukGsYds6aMSCWrEAJ9pUqw/C9B3hAmILYKl7Ljz3N9SFvekD3Q==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-common@5.49.2': + resolution: {integrity: sha512-bn0biLequn3epobCfjUqCxlIlurLr4RHu7RaE4trgN+RDcUq6HCVC3/yqq1hwbNYpVtulnTOJzcaxYlSr1fnuw==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-insights@5.49.2': + resolution: {integrity: sha512-z14wfFs1T3eeYbCArC8pvntAWsPo9f6hnUGoj8IoRUJTwgJiiySECkm8bmmV47/x0oGHfsVn3kBdjMX0yq0sNA==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-personalization@5.49.2': + resolution: {integrity: sha512-GpRf7yuuAX93+Qt0JGEJZwgtL0MFdjFO9n7dn8s2pA9mTjzl0Sc5+uTk1VPbIAuf7xhCP9Mve+URGb6J+EYxgA==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-query-suggestions@5.49.2': + resolution: {integrity: sha512-HZwApmNkp0DiAjZcLYdQLddcG4Agb88OkojiAHGgcm5DVXobT5uSZ9lmyrbw/tmQBJwgu2CNw4zTyXoIB7YbPA==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-search@5.49.2': + resolution: {integrity: sha512-y1IOpG6OSmTpGg/CT0YBb/EAhR2nsC18QWp9Jy8HO9iGySpcwaTvs5kHa17daP3BMTwWyaX9/1tDTDQshZzXdg==} + engines: {node: '>= 14.0.0'} + + '@algolia/ingestion@1.49.2': + resolution: {integrity: sha512-YYJRjaZ2bqk923HxE4um7j/Cm3/xoSkF2HC2ZweOF8cXL3sqnlndSUYmCaxHFjNPWLaSHk2IfssX6J/tdKTULw==} + engines: {node: '>= 14.0.0'} + + '@algolia/monitoring@1.49.2': + resolution: {integrity: sha512-9WgH+Dha39EQQyGKCHlGYnxW/7W19DIrEbCEbnzwAMpGAv1yTWCHMPXHxYa+LcL3eCp2V/5idD1zHNlIKmHRHg==} + engines: {node: '>= 14.0.0'} + + '@algolia/recommend@5.49.2': + resolution: {integrity: sha512-K7Gp5u+JtVYgaVpBxF5rGiM+Ia8SsMdcAJMTDV93rwh00DKNllC19o1g+PwrDjDvyXNrnTEbofzbTs2GLfFyKA==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-browser-xhr@5.49.2': + resolution: {integrity: sha512-3UhYCcWX6fbtN8ABcxZlhaQEwXFh3CsFtARyyadQShHMPe3mJV9Wel4FpJTa+seugRkbezFz0tt6aPTZSYTBuA==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-fetch@5.49.2': + resolution: {integrity: sha512-G94VKSGbsr+WjsDDOBe5QDQ82QYgxvpxRGJfCHZBnYKYsy/jv9qGIDb93biza+LJWizQBUtDj7bZzp3QZyzhPQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-node-http@5.49.2': + resolution: {integrity: sha512-UuihBGHafG/ENsrcTGAn5rsOffrCIRuHMOsD85fZGLEY92ate+BMTUqxz60dv5zerh8ZumN4bRm8eW2z9L11jA==} + engines: {node: '>= 14.0.0'} + '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} @@ -1031,6 +1101,40 @@ packages: resolution: {integrity: sha512-Y6+WUMsTFWE5jb20IFP4YGa5IrGY/+a/FbOSjDF/wz9gepU2hwCYSXRHP/vPwBvwcY3SVMASt4yXxbXNXigmZQ==} engines: {node: '>=18'} + '@docsearch/core@4.6.0': + resolution: {integrity: sha512-IqG3oSd529jVRQ4dWZQKwZwQLVd//bWJTz2HiL0LkiHrI4U/vLrBasKB7lwQB/69nBAcCgs3TmudxTZSLH/ZQg==} + peerDependencies: + '@types/react': '>= 16.8.0 < 20.0.0' + react: '>= 16.8.0 < 20.0.0' + react-dom: '>= 16.8.0 < 20.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + react-dom: + optional: true + + '@docsearch/css@4.6.0': + resolution: {integrity: sha512-YlcAimkXclvqta47g47efzCM5CFxDwv2ClkDfEs/fC/Ak0OxPH2b3czwa4o8O1TRBf+ujFF2RiUwszz2fPVNJQ==} + + '@docsearch/react@4.6.0': + resolution: {integrity: sha512-j8H5B4ArGxBPBWvw3X0J0Rm/Pjv2JDa2rV5OE0DLTp5oiBCptIJ/YlNOhZxuzbO2nwge+o3Z52nJRi3hryK9cA==} + peerDependencies: + '@types/react': '>= 16.8.0 < 20.0.0' + react: '>= 16.8.0 < 20.0.0' + react-dom: '>= 16.8.0 < 20.0.0' + search-insights: '>= 1 < 3' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + react-dom: + optional: true + search-insights: + optional: true + '@electric-sql/pglite@0.3.15': resolution: {integrity: sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ==} @@ -2274,9 +2378,6 @@ packages: '@oxc-project/types@0.95.0': resolution: {integrity: sha512-vACy7vhpMPhjEJhULNxrdR0D943TkA/MigMpJCHmBHvMXxRStRi/dPtTlfQ3uDwWSzRpT8z+7ImjZVf8JWBocQ==} - '@pagefind/default-ui@1.4.0': - resolution: {integrity: sha512-wie82VWn3cnGEdIjh4YwNESyS1G6vRHwL6cNjy9CFgNnWW/PGRjsLq300xjVH5sfPFK3iK36UxvIBymtQIEiSQ==} - '@parcel/watcher-android-arm64@2.5.6': resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} engines: {node: '>= 10.0.0'} @@ -3721,6 +3822,10 @@ packages: ajv@8.18.0: resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + algoliasearch@5.49.2: + resolution: {integrity: sha512-1K0wtDaRONwfhL4h8bbJ9qTjmY6rhGgRvvagXkMBsAOMNr+3Q2SffHECh9DIuNVrMA1JwA0zCwhyepgBZVakng==} + engines: {node: '>= 14.0.0'} + ansi-align@3.0.1: resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} @@ -6975,6 +7080,9 @@ packages: schema-dts@1.1.5: resolution: {integrity: sha512-RJr9EaCmsLzBX2NDiO5Z3ux2BVosNZN5jo0gWgsyKvxKIUL5R3swNvoorulAeL9kLB0iTSX7V6aokhla2m7xbg==} + search-insights@2.17.3: + resolution: {integrity: sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -8208,6 +8316,112 @@ snapshots: '@adobe/css-tools@4.4.4': {} + '@algolia/abtesting@1.15.2': + dependencies: + '@algolia/client-common': 5.49.2 + '@algolia/requester-browser-xhr': 5.49.2 + '@algolia/requester-fetch': 5.49.2 + '@algolia/requester-node-http': 5.49.2 + + '@algolia/autocomplete-core@1.19.2(@algolia/client-search@5.49.2)(algoliasearch@5.49.2)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-plugin-algolia-insights': 1.19.2(@algolia/client-search@5.49.2)(algoliasearch@5.49.2)(search-insights@2.17.3) + '@algolia/autocomplete-shared': 1.19.2(@algolia/client-search@5.49.2)(algoliasearch@5.49.2) + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + - search-insights + + '@algolia/autocomplete-plugin-algolia-insights@1.19.2(@algolia/client-search@5.49.2)(algoliasearch@5.49.2)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-shared': 1.19.2(@algolia/client-search@5.49.2)(algoliasearch@5.49.2) + search-insights: 2.17.3 + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + + '@algolia/autocomplete-shared@1.19.2(@algolia/client-search@5.49.2)(algoliasearch@5.49.2)': + dependencies: + '@algolia/client-search': 5.49.2 + algoliasearch: 5.49.2 + + '@algolia/client-abtesting@5.49.2': + dependencies: + '@algolia/client-common': 5.49.2 + '@algolia/requester-browser-xhr': 5.49.2 + '@algolia/requester-fetch': 5.49.2 + '@algolia/requester-node-http': 5.49.2 + + '@algolia/client-analytics@5.49.2': + dependencies: + '@algolia/client-common': 5.49.2 + '@algolia/requester-browser-xhr': 5.49.2 + '@algolia/requester-fetch': 5.49.2 + '@algolia/requester-node-http': 5.49.2 + + '@algolia/client-common@5.49.2': {} + + '@algolia/client-insights@5.49.2': + dependencies: + '@algolia/client-common': 5.49.2 + '@algolia/requester-browser-xhr': 5.49.2 + '@algolia/requester-fetch': 5.49.2 + '@algolia/requester-node-http': 5.49.2 + + '@algolia/client-personalization@5.49.2': + dependencies: + '@algolia/client-common': 5.49.2 + '@algolia/requester-browser-xhr': 5.49.2 + '@algolia/requester-fetch': 5.49.2 + '@algolia/requester-node-http': 5.49.2 + + '@algolia/client-query-suggestions@5.49.2': + dependencies: + '@algolia/client-common': 5.49.2 + '@algolia/requester-browser-xhr': 5.49.2 + '@algolia/requester-fetch': 5.49.2 + '@algolia/requester-node-http': 5.49.2 + + '@algolia/client-search@5.49.2': + dependencies: + '@algolia/client-common': 5.49.2 + '@algolia/requester-browser-xhr': 5.49.2 + '@algolia/requester-fetch': 5.49.2 + '@algolia/requester-node-http': 5.49.2 + + '@algolia/ingestion@1.49.2': + dependencies: + '@algolia/client-common': 5.49.2 + '@algolia/requester-browser-xhr': 5.49.2 + '@algolia/requester-fetch': 5.49.2 + '@algolia/requester-node-http': 5.49.2 + + '@algolia/monitoring@1.49.2': + dependencies: + '@algolia/client-common': 5.49.2 + '@algolia/requester-browser-xhr': 5.49.2 + '@algolia/requester-fetch': 5.49.2 + '@algolia/requester-node-http': 5.49.2 + + '@algolia/recommend@5.49.2': + dependencies: + '@algolia/client-common': 5.49.2 + '@algolia/requester-browser-xhr': 5.49.2 + '@algolia/requester-fetch': 5.49.2 + '@algolia/requester-node-http': 5.49.2 + + '@algolia/requester-browser-xhr@5.49.2': + dependencies: + '@algolia/client-common': 5.49.2 + + '@algolia/requester-fetch@5.49.2': + dependencies: + '@algolia/client-common': 5.49.2 + + '@algolia/requester-node-http@5.49.2': + dependencies: + '@algolia/client-common': 5.49.2 + '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.13 @@ -8796,6 +9010,28 @@ snapshots: gonzales-pe: 4.3.0 node-source-walk: 7.0.1 + '@docsearch/core@4.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + optionalDependencies: + '@types/react': 19.2.14 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@docsearch/css@4.6.0': {} + + '@docsearch/react@4.6.0(@algolia/client-search@5.49.2)(@types/react@19.2.14)(algoliasearch@5.49.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-core': 1.19.2(@algolia/client-search@5.49.2)(algoliasearch@5.49.2)(search-insights@2.17.3) + '@docsearch/core': 4.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@docsearch/css': 4.6.0 + optionalDependencies: + '@types/react': 19.2.14 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + search-insights: 2.17.3 + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + '@electric-sql/pglite@0.3.15': {} '@emmetio/abbreviation@2.3.3': @@ -10093,8 +10329,6 @@ snapshots: '@oxc-project/types@0.95.0': {} - '@pagefind/default-ui@1.4.0': {} - '@parcel/watcher-android-arm64@2.5.6': optional: true @@ -11187,7 +11421,7 @@ snapshots: magic-string: 0.30.21 sirv: 3.0.2 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.15)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@18.0.1)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.15)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@18.0.1)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) ws: 8.19.0 optionalDependencies: playwright: 1.58.2 @@ -11197,6 +11431,26 @@ snapshots: - utf-8-validate - vite + '@vitest/browser@3.2.4(playwright@1.58.2)(vite@8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4)': + dependencies: + '@testing-library/dom': 10.4.1 + '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) + '@vitest/mocker': 3.2.4(vite@8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/utils': 3.2.4 + magic-string: 0.30.21 + sirv: 3.0.2 + tinyrainbow: 2.0.0 + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.15)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@18.0.1)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + ws: 8.19.0 + optionalDependencies: + playwright: 1.58.2 + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + optional: true + '@vitest/coverage-v8@3.2.4(@vitest/browser@3.2.4)(vitest@3.2.4)': dependencies: '@ampproject/remapping': 2.3.0 @@ -11212,9 +11466,9 @@ snapshots: std-env: 3.10.0 test-exclude: 7.0.2 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.15)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@18.0.1)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.15)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@18.0.1)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) optionalDependencies: - '@vitest/browser': 3.2.4(playwright@1.58.2)(vite@6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4) + '@vitest/browser': 3.2.4(playwright@1.58.2)(vite@8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4) transitivePeerDependencies: - supports-color @@ -11234,6 +11488,15 @@ snapshots: optionalDependencies: vite: 6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + '@vitest/mocker@3.2.4(vite@8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) + optional: true + '@vitest/pretty-format@3.2.4': dependencies: tinyrainbow: 2.0.0 @@ -11263,7 +11526,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.15)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@18.0.1)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.15)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@18.0.1)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) '@vitest/utils@3.2.4': dependencies: @@ -11425,6 +11688,23 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + algoliasearch@5.49.2: + dependencies: + '@algolia/abtesting': 1.15.2 + '@algolia/client-abtesting': 5.49.2 + '@algolia/client-analytics': 5.49.2 + '@algolia/client-common': 5.49.2 + '@algolia/client-insights': 5.49.2 + '@algolia/client-personalization': 5.49.2 + '@algolia/client-query-suggestions': 5.49.2 + '@algolia/client-search': 5.49.2 + '@algolia/ingestion': 1.49.2 + '@algolia/monitoring': 1.49.2 + '@algolia/recommend': 5.49.2 + '@algolia/requester-browser-xhr': 5.49.2 + '@algolia/requester-fetch': 5.49.2 + '@algolia/requester-node-http': 5.49.2 + ansi-align@3.0.1: dependencies: string-width: 4.2.3 @@ -15335,6 +15615,8 @@ snapshots: schema-dts@1.1.5: {} + search-insights@2.17.3: {} + semver@6.3.1: {} semver@7.7.4: {} @@ -16254,7 +16536,7 @@ snapshots: optionalDependencies: '@types/debug': 4.1.12 '@types/node': 22.19.15 - '@vitest/browser': 3.2.4(playwright@1.58.2)(vite@6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4) + '@vitest/browser': 3.2.4(playwright@1.58.2)(vite@8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4) '@vitest/ui': 3.2.4(vitest@3.2.4) happy-dom: 18.0.1 jsdom: 27.4.0 diff --git a/site/CLAUDE.md b/site/CLAUDE.md index b1844bd67..8ae67afa1 100644 --- a/site/CLAUDE.md +++ b/site/CLAUDE.md @@ -165,7 +165,6 @@ site/ │ └── api-docs-builder/ # Generates API reference from TypeScript ├── public/ # Static assets (served untransformed) ├── integrations/ # Custom Astro integrations -│ ├── pagefind.ts # Pagefind search integration │ └── llms-markdown.ts # LLM-optimized markdown generation ├── astro.config.mjs # Astro configuration ├── tsconfig.json # TypeScript config with path aliases @@ -554,7 +553,7 @@ vi.mock('@/types/docs', async () => { - **[Tailwind v4](https://tailwindcss.com)**: CSS utility classes via `@tailwindcss/vite` - **[Nanostores 1.0.1](https://github.com/nanostores/nanostores)**: Cross-island state - **[Base UI 1.2.0](https://base-ui.com)**: Headless accessible components -- **[Pagefind 1.4.0](https://pagefind.app)**: Static search with build-time indexing +- **[Algolia DocSearch v4](https://docsearch.algolia.com)**: Search via Algolia-hosted indexes (docs + blog) - **[Shiki 3.13.0](https://shiki.style)**: Syntax highlighting - **[Vitest 3.2.4](https://vitest.dev)**: Testing framework - **[clsx](https://github.com/lukeed/clsx)**: Class name concatenation utility @@ -582,30 +581,26 @@ import UtilReference from '@/components/docs/api-reference/UtilReference.astro'; ``` -## Custom Astro Integration: Pagefind +## Search: Algolia DocSearch v4 -**Location:** `integrations/pagefind.ts` +**Config:** `src/search.config.ts` — Algolia app ID, API key, index names, Ask AI assistant ID. -**Purpose:** Integrates Pagefind static search into Astro build pipeline. +**Components:** `src/components/Search/Search.tsx` + `Search.astro` -**Development mode:** -- Serves Pagefind index from previous production build -- Uses `sirv` middleware to serve `/pagefind/*` routes -- Warns if index doesn't exist (needs `pnpm build` first) - -**Production mode:** -- Runs Pagefind CLI after Astro build completes -- Indexes all HTML files in `dist/` -- Maps Astro logger levels to Pagefind CLI flags +**How it works:** +- `DocSearch` React component provides the trigger button, modal, and keyboard shortcut (Cmd+K) +- Two indexes queried via the `indices` prop: `videojs_docs` (filtered by current framework) and `videojs_blog` (unfiltered) +- Ask AI enabled via the `askAi` prop with an Algolia assistant +- CSS variable overrides in `Search.astro` map DocSearch theming to site tokens +- No build-time indexing — search queries hit the Algolia API directly -**Usage in `astro.config.mjs`:** -```js -import pagefind from './integrations/pagefind'; +**Crawler config:** See `algolia-crawler-config.md` for Algolia Dashboard settings. -export default defineConfig({ - integrations: [pagefind()], -}); -``` +**Data attributes used by the crawler:** +- `data-search-content` — marks searchable content regions +- `data-search-ignore` — excludes elements from search indexing +- `data-framework`, `data-site`, `data-category` — facet/filter attributes +- `data-llms-*` attributes are for the LLM markdown integration (unrelated to search) ## Environment Variables @@ -819,12 +814,9 @@ Key points: - Add new guides to `src/docs.config.ts` sidebar - Use `devOnly: true` for internal documentation -### Search Indexing +### Search -Pagefind indexes HTML files after build. During development: -1. Run `pnpm build` at least once to generate search index -2. Dev server serves the index from previous build -3. Search won't include new content until next build +Search is powered by Algolia DocSearch v4 and queries the Algolia API directly — no build-time indexing step needed. Search works in both dev and production as long as the Algolia account is provisioned. See the "Search: Algolia DocSearch v4" section above for details. ### Adding Framework/Style Support diff --git a/site/README.md b/site/README.md index b1fa9bbe5..b0a7b17ce 100644 --- a/site/README.md +++ b/site/README.md @@ -13,7 +13,7 @@ Mostly a standard [Astro](https://astro.build/) project. ├── public/ # Static assets served as videojs.org/[filename] ├── scripts/ │ └── api-docs-builder/ # Generates API reference JSON from TypeScript sources -├── integrations/ # Custom Astro integrations (pagefind, llms-markdown, etc.) +├── integrations/ # Custom Astro integrations (llms-markdown, etc.) ├── src/ │ ├── assets/ # Assets imported into components, pages, etc. │ ├── components/ @@ -164,9 +164,14 @@ See [CLAUDE.md](CLAUDE.md) for details on each plugin. ## Custom Integrations -Two custom Astro integrations in `integrations/`: +### Search + +Search is powered by [Algolia DocSearch v4](https://docsearch.algolia.com). Configuration is in `src/search.config.ts` and the component lives at `src/components/Search/`. See `algolia-crawler-config.md` for crawler setup. + +### Custom Integration + +One custom Astro integration in `integrations/`: -- **pagefind** — Indexes HTML after build for static search; serves previous index in dev - **llms-markdown** — Generates LLM-optimized `.md` files and `llms.txt` index from `[data-llms-content]` elements See [CLAUDE.md](CLAUDE.md) for implementation details. diff --git a/site/astro.config.mjs b/site/astro.config.mjs index 8a21849d8..7ce1b3757 100644 --- a/site/astro.config.mjs +++ b/site/astro.config.mjs @@ -11,7 +11,6 @@ import tailwindcss from '@tailwindcss/vite'; import { defineConfig, envField, fontProviders } from 'astro/config'; import svgr from 'vite-plugin-svgr'; import llmsMarkdown from './integrations/llms-markdown'; -import pagefind from './integrations/pagefind'; import rehypePrepareCodeBlocks from './src/utils/rehypePrepareCodeBlocks'; import remarkConditionalHeadings from './src/utils/remarkConditionalHeadings'; import { remarkReadingTime } from './src/utils/remarkReadingTime.mjs'; @@ -77,7 +76,6 @@ export default defineConfig({ `${SITE_URL}/docs/framework/react/llms.txt`, ], }), - pagefind(), llmsMarkdown(), react({ babel: { diff --git a/site/integrations/pagefind.ts b/site/integrations/pagefind.ts deleted file mode 100644 index 029c3e401..000000000 --- a/site/integrations/pagefind.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { spawn } from 'node:child_process'; -import { existsSync } from 'node:fs'; -import { join } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import type { AstroIntegration } from 'astro'; -import sirv from 'sirv'; - -export interface PagefindOptions { - /** - * The path to the built site to index. - * Defaults to the Astro output directory. - */ - site?: string; -} - -export default function pagefind(options: PagefindOptions = {}): AstroIntegration { - return { - name: 'pagefind', - hooks: { - 'astro:server:setup': ({ server, logger }) => { - // Serve Pagefind index from previous build during development - const rootDir = server.config.root; - const indexDir = join(rootDir, 'dist', 'client'); - const pagefindDir = join(indexDir, 'pagefind'); - - // Warn if index doesn't exist yet - if (!existsSync(pagefindDir)) { - logger.warn( - 'Pagefind index not found. Run `pnpm build` first to generate ' + 'the search index for development mode.' - ); - } else { - logger.debug(`Serving Pagefind index from ${indexDir}`); - } - - // Create sirv middleware to serve static files - // approach adapted from https://github.com/shishkin/astro-pagefind - const serve = sirv(indexDir, { - dev: true, // No caching in dev mode - etag: true, // Enable cache validation - }); - - // Mount middleware for /pagefind/* routes only - server.middlewares.use((req, res, next) => { - if (req.url?.startsWith('/pagefind/')) { - serve(req, res, next); - } else { - next(); - } - }); - }, - - 'astro:build:done': async ({ dir, logger }) => { - // Determine the site directory to index - // The dir parameter already points to the correct static output directory - const siteDir = options.site || fileURLToPath(dir); - - // Map Astro logger levels to Pagefind CLI flags - const logLevel = logger.options.level; - const logFlags: string[] = []; - - if (logLevel === 'silent' || logLevel === 'error') { - logFlags.push('--silent'); - } else if (logLevel === 'warn') { - logFlags.push('--quiet'); - } else if (logLevel === 'debug') { - logFlags.push('--verbose'); - } - // 'info' level uses no flag (default) - - logger.info('Running Pagefind indexer...'); - - return new Promise((resolve, reject) => { - const pagefindProcess = spawn('npx', ['-y', 'pagefind', ...logFlags, '--site', siteDir], { - stdio: 'inherit', - shell: true, - }); - - pagefindProcess.on('close', (code) => { - if (code === 0) { - logger.info('Pagefind indexing complete'); - resolve(); - } else { - reject(new Error(`Pagefind process exited with code ${code}`)); - } - }); - - pagefindProcess.on('error', (error) => { - reject(new Error(`Failed to start Pagefind: ${error.message}`)); - }); - }); - }, - }, - }; -} diff --git a/site/package.json b/site/package.json index 3e968e397..a7c4f47a7 100644 --- a/site/package.json +++ b/site/package.json @@ -22,10 +22,11 @@ "@astrojs/rss": "^4.0.12", "@astrojs/sitemap": "^3.6.0", "@base-ui/react": "^1.2.0", + "@docsearch/css": "^4.6.0", + "@docsearch/react": "^4.6.0", "@mux/mux-node": "^12.8.1", "@mux/mux-uploader-react": "^1.4.1", "@nanostores/react": "^1.0.0", - "@pagefind/default-ui": "^1.4.0", "@sentry/astro": "^10.32.1", "@shikijs/transformers": "^4.0.2", "@tailwindcss/vite": "^4.2.1", @@ -70,7 +71,6 @@ "jsdom": "^27.0.0", "prettier": "^3.8.1", "prettier-plugin-astro": "^0.14.1", - "sirv": "^3.0.2", "tsx": "^4.20.3", "turndown": "^7.2.2", "typescript": "^5.9.3", diff --git a/site/src/components/Search/Search.astro b/site/src/components/Search/Search.astro index c26047757..e8049c3d5 100644 --- a/site/src/components/Search/Search.astro +++ b/site/src/components/Search/Search.astro @@ -1,18 +1,6 @@ --- - -import { join } from 'node:path/posix'; -import clsx from 'clsx'; -/** - * We wrap Search.tsx in an .astro entry point for - * 1. easy CSS support, since we can't use Tailwind on pagefind-ui - * 2. a synchronous script to set the meta key (ctrl or cmd) without FOUC or hydration errors - */ +import '@docsearch/css'; import SearchClient from './Search'; -import SearchIcon from './searchIcon.svg'; -import searchIconString from './searchIcon.svg?raw'; - -const baseUrl = import.meta.env.BASE_URL || '/'; -const bundlePath = join(baseUrl, 'pagefind/'); interface Props { class?: string; @@ -20,98 +8,303 @@ interface Props { const { class: className } = Astro.props; --- - - - - - - -{ - /** - * Adapted from https://github.com/withastro/starlight/blob/8a72a19e2cfec235941b4e1401b69b44e6695068/packages/starlight/components/Search.astro#L55C1-L75C1 - * This is intentionally inlined to avoid briefly showing an invalid shortcut. - * Purposely using the deprecated `navigator.platform` property to detect Apple devices, as the - * user agent is spoofed by some browsers when opening the devtools. - */ -} - - diff --git a/site/src/components/Search/index.ts b/site/src/components/Search/index.ts deleted file mode 100644 index ab405b008..000000000 --- a/site/src/components/Search/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './Search.astro'; diff --git a/site/src/components/Tabs.tsx b/site/src/components/Tabs.tsx index d17fbc6a2..a409d3917 100644 --- a/site/src/components/Tabs.tsx +++ b/site/src/components/Tabs.tsx @@ -254,7 +254,7 @@ export function Tab({ value, children, initial, variant = 'compact' }: TabProps) )} {/* to prevent layout shift on state change, we have an invisible bold version of the text preserving space */} - + {children} {children} diff --git a/site/src/search.config.ts b/site/src/search.config.ts index 7196ddbe9..a1650a996 100644 --- a/site/src/search.config.ts +++ b/site/src/search.config.ts @@ -3,5 +3,3 @@ export const DOCSEARCH_APP_ID = '5WD2PWU9HZ'; export const DOCSEARCH_API_KEY = '68c102f304dc6e38fa3c2d167d2389ed'; export const DOCSEARCH_DOCS_INDEX = 'videojs_docs'; export const DOCSEARCH_BLOG_INDEX = 'videojs_blog'; -export const DOCSEARCH_MARKDOWN_INDEX = 'videojs_docs-markdown'; -export const DOCSEARCH_ASK_AI_ASSISTANT_ID = 'PLACEHOLDER_ASSISTANT_ID'; diff --git a/site/src/styles/docsearch.css b/site/src/styles/docsearch.css new file mode 100644 index 000000000..23693d8f8 --- /dev/null +++ b/site/src/styles/docsearch.css @@ -0,0 +1,274 @@ +/* ── Light theme ── */ +:root { + --docsearch-primary-color: var(--color-gold); + --docsearch-text-color: var(--color-faded-black); + --docsearch-secondary-text-color: var(--color-soot); + --docsearch-background-color: var(--color-manila-50); + --docsearch-modal-background: var(--color-manila-50); + --docsearch-modal-shadow: + 0 calc(var(--spacing) * 3) calc(var(--spacing) * 7) color-mix(in srgb, var(--color-faded-black) 15%, transparent), + 0 calc(var(--spacing) / 2) var(--spacing) color-mix(in srgb, var(--color-faded-black) 8%, transparent); + --docsearch-searchbox-background: var(--color-manila-light); + --docsearch-searchbox-focus-background: var(--color-manila-light); + --docsearch-hit-background: var(--color-manila-light); + --docsearch-hit-color: var(--color-faded-black); + --docsearch-hit-highlight-color: color-mix(in srgb, var(--color-gold) 15%, var(--color-manila-light)); + --docsearch-highlight-color: var(--color-orange); + --docsearch-soft-primary-color: color-mix(in srgb, var(--color-gold) 10%, transparent); + --docsearch-focus-color: var(--color-gold); + --docsearch-subtle-color: var(--color-manila-75); + --docsearch-muted-color: var(--color-manila-dark); + --docsearch-icon-color: var(--color-soot); + --docsearch-container-background: color-mix(in srgb, var(--color-faded-black) 20%, transparent); + --docsearch-key-background: var(--color-manila-75); + --docsearch-key-color: var(--color-faded-black); + --docsearch-key-pressed-shadow: inset 0 calc(var(--spacing) / 2) var(--spacing) + color-mix(in srgb, var(--color-faded-black) 15%, transparent); + --docsearch-footer-background: var(--color-manila-light); + --docsearch-footer-shadow: 0 -1px 0 0 var(--color-manila-75); /* 1px hairline */ + --docsearch-logo-color: currentColor; + --docsearch-border-radius: var(--radius-xs); + --docsearch-spacing: calc(var(--spacing) * 4); + --docsearch-searchbox-height: calc(var(--spacing) * 14); + --docsearch-searchbox-initial-height: calc(var(--spacing) * 14); + --docsearch-hit-height: calc(var(--spacing) * 14); + --docsearch-actions-height: calc(var(--spacing) * 11); + --docsearch-footer-height: calc(var(--spacing) * 13); + --docsearch-icon-stroke-width: 2; + --docsearch-search-button-background: transparent; + --docsearch-search-button-text-color: var(--color-faded-black); +} + +/* ── Dark theme ── */ +.dark { + --docsearch-text-color: var(--color-manila-light); + --docsearch-secondary-text-color: var(--color-manila-dark); + --docsearch-background-color: var(--color-warm-gray); + --docsearch-modal-background: var(--color-faded-black); + --docsearch-modal-shadow: + inset 1px 1px 0 0 var(--color-warm-gray), + 0 calc(var(--spacing) * 0.75) calc(var(--spacing) * 2) color-mix(in srgb, var(--color-black) 50%, transparent); + --docsearch-searchbox-background: var(--color-soot); + --docsearch-searchbox-focus-background: var(--color-soot); + --docsearch-hit-background: var(--color-warm-gray); + --docsearch-hit-color: var(--color-manila-light); + --docsearch-hit-highlight-color: color-mix(in srgb, var(--color-gold) 10%, var(--color-warm-gray)); + --docsearch-highlight-color: var(--color-gold); + --docsearch-soft-primary-color: color-mix(in srgb, var(--color-gold) 10%, transparent); + --docsearch-focus-color: var(--color-gold); + --docsearch-subtle-color: var(--color-warm-gray); + --docsearch-muted-color: var(--color-manila-dark); + --docsearch-icon-color: var(--color-manila-dark); + --docsearch-container-background: color-mix(in srgb, var(--color-faded-black) 60%, transparent); + --docsearch-key-background: var(--color-soot); + --docsearch-key-color: var(--color-manila-light); + --docsearch-key-pressed-shadow: inset 0 calc(var(--spacing) / 2) var(--spacing) + color-mix(in srgb, var(--color-black) 40%, transparent); + --docsearch-footer-background: var(--color-soot); + --docsearch-footer-shadow: 0 -1px 0 0 var(--color-warm-gray); /* 1px hairline */ + --docsearch-logo-color: currentColor; + --docsearch-search-button-background: transparent; + --docsearch-search-button-text-color: var(--color-manila-light); +} + +/* ── Trigger button — match rounded-pill navbar style ── */ +.DocSearch-Button { + box-sizing: border-box; + border-radius: 9999px; + border: none; + padding: 0 calc(var(--spacing) * 2); + gap: calc(var(--spacing) * 2); + height: auto; + min-height: calc(var(--spacing) * 6); + margin: 0; + font-family: inherit; +} + +.DocSearch-Button:hover, +.DocSearch-Button:focus-within { + box-shadow: none; +} + +.DocSearch-Button .DocSearch-Search-Icon { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); +} + +.DocSearch-Search-Icon circle, +.DocSearch-Search-Icon path { + stroke-width: var(--docsearch-icon-stroke-width); +} + +.DocSearch-Button .DocSearch-Search-Icon circle, +.DocSearch-Button .DocSearch-Search-Icon path { + stroke-width: calc(var(--docsearch-icon-stroke-width) * 2); +} + +/* Hide button label text on small screens, keep icon + keyboard shortcut */ +.DocSearch-Button-Placeholder { + display: none; +} + +.DocSearch-Button-Keys { + display: none; + min-width: 0; + gap: 0; +} + +.DocSearch-Button .DocSearch-Button-Key { + background: none; + border: none; + box-shadow: none !important; + color: inherit; + width: auto; + height: auto; + font-family: var(--font-display); + font-size: var(--text-h4); + line-height: var(--text-h4--line-height); + letter-spacing: var(--text-h4--letter-spacing); + font-weight: var(--font-weight-bold); + padding: 0; + margin: 0; +} + +@media (width >= 40rem) { + .DocSearch-Button { + border: 1px solid var(--color-faded-black); + min-width: calc(var(--spacing) * 16.5); + } + + .dark .DocSearch-Button { + border-color: var(--color-manila-dark); + } + + .DocSearch-Button:hover, + .DocSearch-Button:focus-within { + border-color: var(--color-manila-dark); + } + + .dark .DocSearch-Button:hover, + .dark .DocSearch-Button:focus-within { + border-color: var(--color-warm-gray); + } + + .DocSearch-Button-Keys { + display: flex; + } + + .DocSearch-Button .DocSearch-Search-Icon { + width: calc(var(--spacing) * 3); + height: calc(var(--spacing) * 3); + } +} + +/* ── Modal backdrop ── */ +.DocSearch-Container { + backdrop-filter: blur(calc(var(--spacing) / 2)); +} + +/* ── Consistent border-radius — DocSearch hardcodes 4px on these ── */ +.DocSearch-Modal { + border-radius: var(--radius-xs); +} + +.DocSearch-Form { + border-radius: var(--radius-xs) var(--radius-xs) 0 0; +} + +.DocSearch-Footer { + border-radius: 0 0 var(--radius-xs) var(--radius-xs); + border-block-start: none; +} + +.DocSearch-Dropdown + .DocSearch-Footer { + border-block-start: 1px solid var(--docsearch-subtle-color); +} + +.DocSearch-Hit a, +.DocSearch-Hit--AskAI { + border-radius: var(--radius-xs); +} + +/* ── Typography — full text-token treatment (size + leading + tracking + weight) ── */ +.DocSearch-Modal { + font-family: var(--font-sans); + font-size: var(--text-p2); + line-height: var(--text-p2--line-height); + letter-spacing: var(--text-p2--letter-spacing); + font-weight: var(--text-p2--font-weight); + text-underline-offset: 0.25em; +} + +.DocSearch-Input { + letter-spacing: var(--tracking-tight); + font-weight: var(--text-p1--font-weight); +} + +.DocSearch-Button-Key, +.DocSearch-Escape-Key { + font-family: var(--font-mono); + font-size: var(--text-code); + line-height: var(--text-code--line-height); + letter-spacing: var(--text-code--letter-spacing); + font-weight: var(--text-code--font-weight); + font-variant-ligatures: none; + text-underline-offset: 0.125em; +} + +.DocSearch-Hit-source { + font-size: var(--text-p2); + line-height: var(--text-p2--line-height); + letter-spacing: var(--text-p2--letter-spacing); + font-weight: var(--font-weight-bold); + padding-block-start: calc(var(--spacing) * 4); + padding-block-end: calc(var(--spacing) * 2); +} + +.DocSearch-Hit-title { + font-size: var(--text-p2); + line-height: var(--text-p2--line-height); + letter-spacing: var(--text-p2--letter-spacing); +} + +.DocSearch-Hit-path { + font-size: var(--text-p3); + line-height: var(--text-p3--line-height); + letter-spacing: var(--text-p3--letter-spacing); + font-weight: var(--text-p3--font-weight); +} + +.DocSearch-Label, +.DocSearch-Help, +.DocSearch-NoResults-Help { + font-size: var(--text-p3); + line-height: var(--text-p3--line-height); + letter-spacing: var(--text-p3--letter-spacing); + font-weight: var(--text-p3--font-weight); +} + +.DocSearch-Clear { + font-size: var(--text-p3); + font-weight: var(--text-p3--font-weight); +} + +.DocSearch-Title strong { + font-weight: var(--font-weight-semibold); +} + +/* ── Hit spacing — more breathing room, tighter title/path gap ── */ +.DocSearch-Hit-content-wrapper { + gap: 0; +} + +/* ── Algolia logo — SVG paths hardcode blue fills via .cls-1/.cls-2 ── */ +.DocSearch-Logo svg .cls-1, +.DocSearch-Logo svg .cls-2 { + fill: var(--docsearch-logo-color); +} + +/* ── Text match highlight — keep readable in all states ── */ +.DocSearch-Hit mark, +.DocSearch-Hit[aria-selected="true"] mark { + text-decoration-color: var(--color-gold); + color: currentColor; +} diff --git a/site/src/styles/globals.css b/site/src/styles/globals.css index cfa71aaa7..ae254e15b 100644 --- a/site/src/styles/globals.css +++ b/site/src/styles/globals.css @@ -1,5 +1,6 @@ @import "tailwindcss"; @import "./shiki-transformers.css"; +@import "./docsearch.css"; /** * @license From c3280f5f4dcb286b7a3b39b74bc472b3e9716ea4 Mon Sep 17 00:00:00 2001 From: Darius Cepulis Date: Tue, 17 Mar 2026 15:24:54 -0500 Subject: [PATCH 3/6] docs(site): update search docs to match current implementation Remove stale references to Ask AI, algolia-crawler-config.md, and old Search/ directory structure. Fix component paths and remove dead .DocSearch-Hit--AskAI CSS selector. Co-Authored-By: Claude Opus 4.6 (1M context) --- site/CLAUDE.md | 10 ++++------ site/README.md | 2 +- site/src/styles/docsearch.css | 3 +-- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/site/CLAUDE.md b/site/CLAUDE.md index 8ae67afa1..344de9ccb 100644 --- a/site/CLAUDE.md +++ b/site/CLAUDE.md @@ -583,19 +583,17 @@ import UtilReference from '@/components/docs/api-reference/UtilReference.astro'; ## Search: Algolia DocSearch v4 -**Config:** `src/search.config.ts` — Algolia app ID, API key, index names, Ask AI assistant ID. +**Config:** `src/search.config.ts` — Algolia app ID, API key, and index names. -**Components:** `src/components/Search/Search.tsx` + `Search.astro` +**Component:** `src/components/Search.tsx` — React component loaded via `client:load` in the NavBar. + +**Styles:** `src/styles/docsearch.css` — CSS variable overrides mapping DocSearch theming to site tokens (imported via `globals.css`). **How it works:** - `DocSearch` React component provides the trigger button, modal, and keyboard shortcut (Cmd+K) - Two indexes queried via the `indices` prop: `videojs_docs` (filtered by current framework) and `videojs_blog` (unfiltered) -- Ask AI enabled via the `askAi` prop with an Algolia assistant -- CSS variable overrides in `Search.astro` map DocSearch theming to site tokens - No build-time indexing — search queries hit the Algolia API directly -**Crawler config:** See `algolia-crawler-config.md` for Algolia Dashboard settings. - **Data attributes used by the crawler:** - `data-search-content` — marks searchable content regions - `data-search-ignore` — excludes elements from search indexing diff --git a/site/README.md b/site/README.md index b0a7b17ce..9ac80beb4 100644 --- a/site/README.md +++ b/site/README.md @@ -166,7 +166,7 @@ See [CLAUDE.md](CLAUDE.md) for details on each plugin. ### Search -Search is powered by [Algolia DocSearch v4](https://docsearch.algolia.com). Configuration is in `src/search.config.ts` and the component lives at `src/components/Search/`. See `algolia-crawler-config.md` for crawler setup. +Search is powered by [Algolia DocSearch v4](https://docsearch.algolia.com). Configuration is in `src/search.config.ts` and the component is `src/components/Search.tsx`. ### Custom Integration diff --git a/site/src/styles/docsearch.css b/site/src/styles/docsearch.css index 23693d8f8..f4386f889 100644 --- a/site/src/styles/docsearch.css +++ b/site/src/styles/docsearch.css @@ -184,8 +184,7 @@ border-block-start: 1px solid var(--docsearch-subtle-color); } -.DocSearch-Hit a, -.DocSearch-Hit--AskAI { +.DocSearch-Hit a { border-radius: var(--radius-xs); } From 6c26bc0fb650bfcb5f19b8351c78e76306a48fdb Mon Sep 17 00:00:00 2001 From: Darius Cepulis Date: Fri, 20 Mar 2026 09:49:42 -0500 Subject: [PATCH 4/6] fix(site): fix docsearch CSS cascade with proper layer ordering Move @docsearch/css import from JS (Search.tsx) into docsearch.css with layer(base) to ensure deterministic load order and correct cascade priority with Tailwind utilities. Co-Authored-By: Claude Opus 4.6 (1M context) --- site/src/components/Search.tsx | 1 - site/src/styles/docsearch.css | 480 +++++++++++++++++---------------- 2 files changed, 242 insertions(+), 239 deletions(-) diff --git a/site/src/components/Search.tsx b/site/src/components/Search.tsx index d74f752b1..a640817f9 100644 --- a/site/src/components/Search.tsx +++ b/site/src/components/Search.tsx @@ -3,7 +3,6 @@ import { useStore } from '@nanostores/react'; import { GITHUB_REPO_URL } from '@/consts'; import { DOCSEARCH_API_KEY, DOCSEARCH_APP_ID, DOCSEARCH_BLOG_INDEX, DOCSEARCH_DOCS_INDEX } from '@/search.config'; import { currentFramework } from '@/stores/preferences'; -import '@docsearch/css'; interface SearchProps { className?: string; diff --git a/site/src/styles/docsearch.css b/site/src/styles/docsearch.css index f4386f889..fb7af13fe 100644 --- a/site/src/styles/docsearch.css +++ b/site/src/styles/docsearch.css @@ -1,273 +1,277 @@ -/* ── Light theme ── */ -:root { - --docsearch-primary-color: var(--color-gold); - --docsearch-text-color: var(--color-faded-black); - --docsearch-secondary-text-color: var(--color-soot); - --docsearch-background-color: var(--color-manila-50); - --docsearch-modal-background: var(--color-manila-50); - --docsearch-modal-shadow: - 0 calc(var(--spacing) * 3) calc(var(--spacing) * 7) color-mix(in srgb, var(--color-faded-black) 15%, transparent), - 0 calc(var(--spacing) / 2) var(--spacing) color-mix(in srgb, var(--color-faded-black) 8%, transparent); - --docsearch-searchbox-background: var(--color-manila-light); - --docsearch-searchbox-focus-background: var(--color-manila-light); - --docsearch-hit-background: var(--color-manila-light); - --docsearch-hit-color: var(--color-faded-black); - --docsearch-hit-highlight-color: color-mix(in srgb, var(--color-gold) 15%, var(--color-manila-light)); - --docsearch-highlight-color: var(--color-orange); - --docsearch-soft-primary-color: color-mix(in srgb, var(--color-gold) 10%, transparent); - --docsearch-focus-color: var(--color-gold); - --docsearch-subtle-color: var(--color-manila-75); - --docsearch-muted-color: var(--color-manila-dark); - --docsearch-icon-color: var(--color-soot); - --docsearch-container-background: color-mix(in srgb, var(--color-faded-black) 20%, transparent); - --docsearch-key-background: var(--color-manila-75); - --docsearch-key-color: var(--color-faded-black); - --docsearch-key-pressed-shadow: inset 0 calc(var(--spacing) / 2) var(--spacing) - color-mix(in srgb, var(--color-faded-black) 15%, transparent); - --docsearch-footer-background: var(--color-manila-light); - --docsearch-footer-shadow: 0 -1px 0 0 var(--color-manila-75); /* 1px hairline */ - --docsearch-logo-color: currentColor; - --docsearch-border-radius: var(--radius-xs); - --docsearch-spacing: calc(var(--spacing) * 4); - --docsearch-searchbox-height: calc(var(--spacing) * 14); - --docsearch-searchbox-initial-height: calc(var(--spacing) * 14); - --docsearch-hit-height: calc(var(--spacing) * 14); - --docsearch-actions-height: calc(var(--spacing) * 11); - --docsearch-footer-height: calc(var(--spacing) * 13); - --docsearch-icon-stroke-width: 2; - --docsearch-search-button-background: transparent; - --docsearch-search-button-text-color: var(--color-faded-black); -} - -/* ── Dark theme ── */ -.dark { - --docsearch-text-color: var(--color-manila-light); - --docsearch-secondary-text-color: var(--color-manila-dark); - --docsearch-background-color: var(--color-warm-gray); - --docsearch-modal-background: var(--color-faded-black); - --docsearch-modal-shadow: - inset 1px 1px 0 0 var(--color-warm-gray), - 0 calc(var(--spacing) * 0.75) calc(var(--spacing) * 2) color-mix(in srgb, var(--color-black) 50%, transparent); - --docsearch-searchbox-background: var(--color-soot); - --docsearch-searchbox-focus-background: var(--color-soot); - --docsearch-hit-background: var(--color-warm-gray); - --docsearch-hit-color: var(--color-manila-light); - --docsearch-hit-highlight-color: color-mix(in srgb, var(--color-gold) 10%, var(--color-warm-gray)); - --docsearch-highlight-color: var(--color-gold); - --docsearch-soft-primary-color: color-mix(in srgb, var(--color-gold) 10%, transparent); - --docsearch-focus-color: var(--color-gold); - --docsearch-subtle-color: var(--color-warm-gray); - --docsearch-muted-color: var(--color-manila-dark); - --docsearch-icon-color: var(--color-manila-dark); - --docsearch-container-background: color-mix(in srgb, var(--color-faded-black) 60%, transparent); - --docsearch-key-background: var(--color-soot); - --docsearch-key-color: var(--color-manila-light); - --docsearch-key-pressed-shadow: inset 0 calc(var(--spacing) / 2) var(--spacing) - color-mix(in srgb, var(--color-black) 40%, transparent); - --docsearch-footer-background: var(--color-soot); - --docsearch-footer-shadow: 0 -1px 0 0 var(--color-warm-gray); /* 1px hairline */ - --docsearch-logo-color: currentColor; - --docsearch-search-button-background: transparent; - --docsearch-search-button-text-color: var(--color-manila-light); -} - -/* ── Trigger button — match rounded-pill navbar style ── */ -.DocSearch-Button { - box-sizing: border-box; - border-radius: 9999px; - border: none; - padding: 0 calc(var(--spacing) * 2); - gap: calc(var(--spacing) * 2); - height: auto; - min-height: calc(var(--spacing) * 6); - margin: 0; - font-family: inherit; -} - -.DocSearch-Button:hover, -.DocSearch-Button:focus-within { - box-shadow: none; -} - -.DocSearch-Button .DocSearch-Search-Icon { - width: calc(var(--spacing) * 4); - height: calc(var(--spacing) * 4); -} - -.DocSearch-Search-Icon circle, -.DocSearch-Search-Icon path { - stroke-width: var(--docsearch-icon-stroke-width); -} - -.DocSearch-Button .DocSearch-Search-Icon circle, -.DocSearch-Button .DocSearch-Search-Icon path { - stroke-width: calc(var(--docsearch-icon-stroke-width) * 2); -} +@import "@docsearch/css/dist/style.css" layer(base); + +@layer base { + /* ── Light theme ── */ + :root { + --docsearch-primary-color: var(--color-gold); + --docsearch-text-color: var(--color-faded-black); + --docsearch-secondary-text-color: var(--color-soot); + --docsearch-background-color: var(--color-manila-50); + --docsearch-modal-background: var(--color-manila-50); + --docsearch-modal-shadow: + 0 calc(var(--spacing) * 3) calc(var(--spacing) * 7) color-mix(in srgb, var(--color-faded-black) 15%, transparent), + 0 calc(var(--spacing) / 2) var(--spacing) color-mix(in srgb, var(--color-faded-black) 8%, transparent); + --docsearch-searchbox-background: var(--color-manila-light); + --docsearch-searchbox-focus-background: var(--color-manila-light); + --docsearch-hit-background: var(--color-manila-light); + --docsearch-hit-color: var(--color-faded-black); + --docsearch-hit-highlight-color: color-mix(in srgb, var(--color-gold) 15%, var(--color-manila-light)); + --docsearch-highlight-color: var(--color-orange); + --docsearch-soft-primary-color: color-mix(in srgb, var(--color-gold) 10%, transparent); + --docsearch-focus-color: var(--color-gold); + --docsearch-subtle-color: var(--color-manila-75); + --docsearch-muted-color: var(--color-manila-dark); + --docsearch-icon-color: var(--color-soot); + --docsearch-container-background: color-mix(in srgb, var(--color-faded-black) 20%, transparent); + --docsearch-key-background: var(--color-manila-75); + --docsearch-key-color: var(--color-faded-black); + --docsearch-key-pressed-shadow: inset 0 calc(var(--spacing) / 2) var(--spacing) + color-mix(in srgb, var(--color-faded-black) 15%, transparent); + --docsearch-footer-background: var(--color-manila-light); + --docsearch-footer-shadow: 0 -1px 0 0 var(--color-manila-75); /* 1px hairline */ + --docsearch-logo-color: currentColor; + --docsearch-border-radius: var(--radius-xs); + --docsearch-spacing: calc(var(--spacing) * 4); + --docsearch-searchbox-height: calc(var(--spacing) * 14); + --docsearch-searchbox-initial-height: calc(var(--spacing) * 14); + --docsearch-hit-height: calc(var(--spacing) * 14); + --docsearch-actions-height: calc(var(--spacing) * 11); + --docsearch-footer-height: calc(var(--spacing) * 13); + --docsearch-icon-stroke-width: 2; + --docsearch-search-button-background: transparent; + --docsearch-search-button-text-color: var(--color-faded-black); + } -/* Hide button label text on small screens, keep icon + keyboard shortcut */ -.DocSearch-Button-Placeholder { - display: none; -} + /* ── Dark theme ── */ + .dark { + --docsearch-text-color: var(--color-manila-light); + --docsearch-secondary-text-color: var(--color-manila-dark); + --docsearch-background-color: var(--color-warm-gray); + --docsearch-modal-background: var(--color-faded-black); + --docsearch-modal-shadow: + inset 1px 1px 0 0 var(--color-warm-gray), + 0 calc(var(--spacing) * 0.75) calc(var(--spacing) * 2) color-mix(in srgb, var(--color-black) 50%, transparent); + --docsearch-searchbox-background: var(--color-soot); + --docsearch-searchbox-focus-background: var(--color-soot); + --docsearch-hit-background: var(--color-warm-gray); + --docsearch-hit-color: var(--color-manila-light); + --docsearch-hit-highlight-color: color-mix(in srgb, var(--color-gold) 10%, var(--color-warm-gray)); + --docsearch-highlight-color: var(--color-gold); + --docsearch-soft-primary-color: color-mix(in srgb, var(--color-gold) 10%, transparent); + --docsearch-focus-color: var(--color-gold); + --docsearch-subtle-color: var(--color-warm-gray); + --docsearch-muted-color: var(--color-manila-dark); + --docsearch-icon-color: var(--color-manila-dark); + --docsearch-container-background: color-mix(in srgb, var(--color-faded-black) 60%, transparent); + --docsearch-key-background: var(--color-soot); + --docsearch-key-color: var(--color-manila-light); + --docsearch-key-pressed-shadow: inset 0 calc(var(--spacing) / 2) var(--spacing) + color-mix(in srgb, var(--color-black) 40%, transparent); + --docsearch-footer-background: var(--color-soot); + --docsearch-footer-shadow: 0 -1px 0 0 var(--color-warm-gray); /* 1px hairline */ + --docsearch-logo-color: currentColor; + --docsearch-search-button-background: transparent; + --docsearch-search-button-text-color: var(--color-manila-light); + } -.DocSearch-Button-Keys { - display: none; - min-width: 0; - gap: 0; -} + /* ── Trigger button — match rounded-pill navbar style ── */ + .DocSearch-Button { + box-sizing: border-box; + border-radius: 9999px; + border: none; + padding: 0 calc(var(--spacing) * 2); + gap: calc(var(--spacing) * 2); + height: auto; + min-height: calc(var(--spacing) * 6); + margin: 0; + font-family: inherit; + } -.DocSearch-Button .DocSearch-Button-Key { - background: none; - border: none; - box-shadow: none !important; - color: inherit; - width: auto; - height: auto; - font-family: var(--font-display); - font-size: var(--text-h4); - line-height: var(--text-h4--line-height); - letter-spacing: var(--text-h4--letter-spacing); - font-weight: var(--font-weight-bold); - padding: 0; - margin: 0; -} + .DocSearch-Button:hover, + .DocSearch-Button:focus-within { + box-shadow: none; + } -@media (width >= 40rem) { - .DocSearch-Button { - border: 1px solid var(--color-faded-black); - min-width: calc(var(--spacing) * 16.5); + .DocSearch-Button .DocSearch-Search-Icon { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); } - .dark .DocSearch-Button { - border-color: var(--color-manila-dark); + .DocSearch-Search-Icon circle, + .DocSearch-Search-Icon path { + stroke-width: var(--docsearch-icon-stroke-width); } - .DocSearch-Button:hover, - .DocSearch-Button:focus-within { - border-color: var(--color-manila-dark); + .DocSearch-Button .DocSearch-Search-Icon circle, + .DocSearch-Button .DocSearch-Search-Icon path { + stroke-width: calc(var(--docsearch-icon-stroke-width) * 2); } - .dark .DocSearch-Button:hover, - .dark .DocSearch-Button:focus-within { - border-color: var(--color-warm-gray); + /* Hide button label text on small screens, keep icon + keyboard shortcut */ + .DocSearch-Button-Placeholder { + display: none; } .DocSearch-Button-Keys { - display: flex; + display: none; + min-width: 0; + gap: 0; } - .DocSearch-Button .DocSearch-Search-Icon { - width: calc(var(--spacing) * 3); - height: calc(var(--spacing) * 3); + .DocSearch-Button .DocSearch-Button-Key { + background: none; + border: none; + box-shadow: none !important; + color: inherit; + width: auto; + height: auto; + font-family: var(--font-display); + font-size: var(--text-h4); + line-height: var(--text-h4--line-height); + letter-spacing: var(--text-h4--letter-spacing); + font-weight: var(--font-weight-bold); + padding: 0; + margin: 0; } -} -/* ── Modal backdrop ── */ -.DocSearch-Container { - backdrop-filter: blur(calc(var(--spacing) / 2)); -} + @media (width >= 40rem) { + .DocSearch-Button { + border: 1px solid var(--color-faded-black); + min-width: calc(var(--spacing) * 16.5); + } + + .dark .DocSearch-Button { + border-color: var(--color-manila-dark); + } + + .DocSearch-Button:hover, + .DocSearch-Button:focus-within { + border-color: var(--color-manila-dark); + } + + .dark .DocSearch-Button:hover, + .dark .DocSearch-Button:focus-within { + border-color: var(--color-warm-gray); + } + + .DocSearch-Button-Keys { + display: flex; + } + + .DocSearch-Button .DocSearch-Search-Icon { + width: calc(var(--spacing) * 3); + height: calc(var(--spacing) * 3); + } + } -/* ── Consistent border-radius — DocSearch hardcodes 4px on these ── */ -.DocSearch-Modal { - border-radius: var(--radius-xs); -} + /* ── Modal backdrop ── */ + .DocSearch-Container { + backdrop-filter: blur(calc(var(--spacing) / 2)); + } -.DocSearch-Form { - border-radius: var(--radius-xs) var(--radius-xs) 0 0; -} + /* ── Consistent border-radius — DocSearch hardcodes 4px on these ── */ + .DocSearch-Modal { + border-radius: var(--radius-xs); + } -.DocSearch-Footer { - border-radius: 0 0 var(--radius-xs) var(--radius-xs); - border-block-start: none; -} + .DocSearch-Form { + border-radius: var(--radius-xs) var(--radius-xs) 0 0; + } -.DocSearch-Dropdown + .DocSearch-Footer { - border-block-start: 1px solid var(--docsearch-subtle-color); -} + .DocSearch-Footer { + border-radius: 0 0 var(--radius-xs) var(--radius-xs); + border-block-start: none; + } -.DocSearch-Hit a { - border-radius: var(--radius-xs); -} + .DocSearch-Dropdown + .DocSearch-Footer { + border-block-start: 1px solid var(--docsearch-subtle-color); + } -/* ── Typography — full text-token treatment (size + leading + tracking + weight) ── */ -.DocSearch-Modal { - font-family: var(--font-sans); - font-size: var(--text-p2); - line-height: var(--text-p2--line-height); - letter-spacing: var(--text-p2--letter-spacing); - font-weight: var(--text-p2--font-weight); - text-underline-offset: 0.25em; -} + .DocSearch-Hit a { + border-radius: var(--radius-xs); + } -.DocSearch-Input { - letter-spacing: var(--tracking-tight); - font-weight: var(--text-p1--font-weight); -} + /* ── Typography — full text-token treatment (size + leading + tracking + weight) ── */ + .DocSearch-Modal { + font-family: var(--font-sans); + font-size: var(--text-p2); + line-height: var(--text-p2--line-height); + letter-spacing: var(--text-p2--letter-spacing); + font-weight: var(--text-p2--font-weight); + text-underline-offset: 0.25em; + } -.DocSearch-Button-Key, -.DocSearch-Escape-Key { - font-family: var(--font-mono); - font-size: var(--text-code); - line-height: var(--text-code--line-height); - letter-spacing: var(--text-code--letter-spacing); - font-weight: var(--text-code--font-weight); - font-variant-ligatures: none; - text-underline-offset: 0.125em; -} + .DocSearch-Input { + letter-spacing: var(--tracking-tight); + font-weight: var(--text-p1--font-weight); + } -.DocSearch-Hit-source { - font-size: var(--text-p2); - line-height: var(--text-p2--line-height); - letter-spacing: var(--text-p2--letter-spacing); - font-weight: var(--font-weight-bold); - padding-block-start: calc(var(--spacing) * 4); - padding-block-end: calc(var(--spacing) * 2); -} + .DocSearch-Button-Key, + .DocSearch-Escape-Key { + font-family: var(--font-mono); + font-size: var(--text-code); + line-height: var(--text-code--line-height); + letter-spacing: var(--text-code--letter-spacing); + font-weight: var(--text-code--font-weight); + font-variant-ligatures: none; + text-underline-offset: 0.125em; + } -.DocSearch-Hit-title { - font-size: var(--text-p2); - line-height: var(--text-p2--line-height); - letter-spacing: var(--text-p2--letter-spacing); -} + .DocSearch-Hit-source { + font-size: var(--text-p2); + line-height: var(--text-p2--line-height); + letter-spacing: var(--text-p2--letter-spacing); + font-weight: var(--font-weight-bold); + padding-block-start: calc(var(--spacing) * 4); + padding-block-end: calc(var(--spacing) * 2); + } -.DocSearch-Hit-path { - font-size: var(--text-p3); - line-height: var(--text-p3--line-height); - letter-spacing: var(--text-p3--letter-spacing); - font-weight: var(--text-p3--font-weight); -} + .DocSearch-Hit-title { + font-size: var(--text-p2); + line-height: var(--text-p2--line-height); + letter-spacing: var(--text-p2--letter-spacing); + } -.DocSearch-Label, -.DocSearch-Help, -.DocSearch-NoResults-Help { - font-size: var(--text-p3); - line-height: var(--text-p3--line-height); - letter-spacing: var(--text-p3--letter-spacing); - font-weight: var(--text-p3--font-weight); -} + .DocSearch-Hit-path { + font-size: var(--text-p3); + line-height: var(--text-p3--line-height); + letter-spacing: var(--text-p3--letter-spacing); + font-weight: var(--text-p3--font-weight); + } -.DocSearch-Clear { - font-size: var(--text-p3); - font-weight: var(--text-p3--font-weight); -} + .DocSearch-Label, + .DocSearch-Help, + .DocSearch-NoResults-Help { + font-size: var(--text-p3); + line-height: var(--text-p3--line-height); + letter-spacing: var(--text-p3--letter-spacing); + font-weight: var(--text-p3--font-weight); + } -.DocSearch-Title strong { - font-weight: var(--font-weight-semibold); -} + .DocSearch-Clear { + font-size: var(--text-p3); + font-weight: var(--text-p3--font-weight); + } -/* ── Hit spacing — more breathing room, tighter title/path gap ── */ -.DocSearch-Hit-content-wrapper { - gap: 0; -} + .DocSearch-Title strong { + font-weight: var(--font-weight-semibold); + } -/* ── Algolia logo — SVG paths hardcode blue fills via .cls-1/.cls-2 ── */ -.DocSearch-Logo svg .cls-1, -.DocSearch-Logo svg .cls-2 { - fill: var(--docsearch-logo-color); -} + /* ── Hit spacing — more breathing room, tighter title/path gap ── */ + .DocSearch-Hit-content-wrapper { + gap: 0; + } + + /* ── Algolia logo — SVG paths hardcode blue fills via .cls-1/.cls-2 ── */ + .DocSearch-Logo svg .cls-1, + .DocSearch-Logo svg .cls-2 { + fill: var(--docsearch-logo-color); + } -/* ── Text match highlight — keep readable in all states ── */ -.DocSearch-Hit mark, -.DocSearch-Hit[aria-selected="true"] mark { - text-decoration-color: var(--color-gold); - color: currentColor; + /* ── Text match highlight — keep readable in all states ── */ + .DocSearch-Hit mark, + .DocSearch-Hit[aria-selected="true"] mark { + text-decoration-color: var(--color-gold); + color: currentColor; + } } From 687046281a74f9fe826cd23c0c071bde97e19b20 Mon Sep 17 00:00:00 2001 From: Darius Cepulis Date: Fri, 20 Mar 2026 09:54:48 -0500 Subject: [PATCH 5/6] wip: on second thought... --- site/src/styles/docsearch.css | 481 +++++++++++++++++----------------- 1 file changed, 239 insertions(+), 242 deletions(-) diff --git a/site/src/styles/docsearch.css b/site/src/styles/docsearch.css index fb7af13fe..f6ecde380 100644 --- a/site/src/styles/docsearch.css +++ b/site/src/styles/docsearch.css @@ -1,277 +1,274 @@ -@import "@docsearch/css/dist/style.css" layer(base); - -@layer base { - /* ── Light theme ── */ - :root { - --docsearch-primary-color: var(--color-gold); - --docsearch-text-color: var(--color-faded-black); - --docsearch-secondary-text-color: var(--color-soot); - --docsearch-background-color: var(--color-manila-50); - --docsearch-modal-background: var(--color-manila-50); - --docsearch-modal-shadow: - 0 calc(var(--spacing) * 3) calc(var(--spacing) * 7) color-mix(in srgb, var(--color-faded-black) 15%, transparent), - 0 calc(var(--spacing) / 2) var(--spacing) color-mix(in srgb, var(--color-faded-black) 8%, transparent); - --docsearch-searchbox-background: var(--color-manila-light); - --docsearch-searchbox-focus-background: var(--color-manila-light); - --docsearch-hit-background: var(--color-manila-light); - --docsearch-hit-color: var(--color-faded-black); - --docsearch-hit-highlight-color: color-mix(in srgb, var(--color-gold) 15%, var(--color-manila-light)); - --docsearch-highlight-color: var(--color-orange); - --docsearch-soft-primary-color: color-mix(in srgb, var(--color-gold) 10%, transparent); - --docsearch-focus-color: var(--color-gold); - --docsearch-subtle-color: var(--color-manila-75); - --docsearch-muted-color: var(--color-manila-dark); - --docsearch-icon-color: var(--color-soot); - --docsearch-container-background: color-mix(in srgb, var(--color-faded-black) 20%, transparent); - --docsearch-key-background: var(--color-manila-75); - --docsearch-key-color: var(--color-faded-black); - --docsearch-key-pressed-shadow: inset 0 calc(var(--spacing) / 2) var(--spacing) - color-mix(in srgb, var(--color-faded-black) 15%, transparent); - --docsearch-footer-background: var(--color-manila-light); - --docsearch-footer-shadow: 0 -1px 0 0 var(--color-manila-75); /* 1px hairline */ - --docsearch-logo-color: currentColor; - --docsearch-border-radius: var(--radius-xs); - --docsearch-spacing: calc(var(--spacing) * 4); - --docsearch-searchbox-height: calc(var(--spacing) * 14); - --docsearch-searchbox-initial-height: calc(var(--spacing) * 14); - --docsearch-hit-height: calc(var(--spacing) * 14); - --docsearch-actions-height: calc(var(--spacing) * 11); - --docsearch-footer-height: calc(var(--spacing) * 13); - --docsearch-icon-stroke-width: 2; - --docsearch-search-button-background: transparent; - --docsearch-search-button-text-color: var(--color-faded-black); - } +@import "@docsearch/css/dist/style.css"; +/* ── Light theme ── */ +:root { + --docsearch-primary-color: var(--color-gold); + --docsearch-text-color: var(--color-faded-black); + --docsearch-secondary-text-color: var(--color-soot); + --docsearch-background-color: var(--color-manila-50); + --docsearch-modal-background: var(--color-manila-50); + --docsearch-modal-shadow: + 0 calc(var(--spacing) * 3) calc(var(--spacing) * 7) color-mix(in srgb, var(--color-faded-black) 15%, transparent), + 0 calc(var(--spacing) / 2) var(--spacing) color-mix(in srgb, var(--color-faded-black) 8%, transparent); + --docsearch-searchbox-background: var(--color-manila-light); + --docsearch-searchbox-focus-background: var(--color-manila-light); + --docsearch-hit-background: var(--color-manila-light); + --docsearch-hit-color: var(--color-faded-black); + --docsearch-hit-highlight-color: color-mix(in srgb, var(--color-gold) 15%, var(--color-manila-light)); + --docsearch-highlight-color: var(--color-orange); + --docsearch-soft-primary-color: color-mix(in srgb, var(--color-gold) 10%, transparent); + --docsearch-focus-color: var(--color-gold); + --docsearch-subtle-color: var(--color-manila-75); + --docsearch-muted-color: var(--color-manila-dark); + --docsearch-icon-color: var(--color-soot); + --docsearch-container-background: color-mix(in srgb, var(--color-faded-black) 20%, transparent); + --docsearch-key-background: var(--color-manila-75); + --docsearch-key-color: var(--color-faded-black); + --docsearch-key-pressed-shadow: inset 0 calc(var(--spacing) / 2) var(--spacing) + color-mix(in srgb, var(--color-faded-black) 15%, transparent); + --docsearch-footer-background: var(--color-manila-light); + --docsearch-footer-shadow: 0 -1px 0 0 var(--color-manila-75); /* 1px hairline */ + --docsearch-logo-color: currentColor; + --docsearch-border-radius: var(--radius-xs); + --docsearch-spacing: calc(var(--spacing) * 4); + --docsearch-searchbox-height: calc(var(--spacing) * 14); + --docsearch-searchbox-initial-height: calc(var(--spacing) * 14); + --docsearch-hit-height: calc(var(--spacing) * 14); + --docsearch-actions-height: calc(var(--spacing) * 11); + --docsearch-footer-height: calc(var(--spacing) * 13); + --docsearch-icon-stroke-width: 2; + --docsearch-search-button-background: transparent; + --docsearch-search-button-text-color: var(--color-faded-black); +} - /* ── Dark theme ── */ - .dark { - --docsearch-text-color: var(--color-manila-light); - --docsearch-secondary-text-color: var(--color-manila-dark); - --docsearch-background-color: var(--color-warm-gray); - --docsearch-modal-background: var(--color-faded-black); - --docsearch-modal-shadow: - inset 1px 1px 0 0 var(--color-warm-gray), - 0 calc(var(--spacing) * 0.75) calc(var(--spacing) * 2) color-mix(in srgb, var(--color-black) 50%, transparent); - --docsearch-searchbox-background: var(--color-soot); - --docsearch-searchbox-focus-background: var(--color-soot); - --docsearch-hit-background: var(--color-warm-gray); - --docsearch-hit-color: var(--color-manila-light); - --docsearch-hit-highlight-color: color-mix(in srgb, var(--color-gold) 10%, var(--color-warm-gray)); - --docsearch-highlight-color: var(--color-gold); - --docsearch-soft-primary-color: color-mix(in srgb, var(--color-gold) 10%, transparent); - --docsearch-focus-color: var(--color-gold); - --docsearch-subtle-color: var(--color-warm-gray); - --docsearch-muted-color: var(--color-manila-dark); - --docsearch-icon-color: var(--color-manila-dark); - --docsearch-container-background: color-mix(in srgb, var(--color-faded-black) 60%, transparent); - --docsearch-key-background: var(--color-soot); - --docsearch-key-color: var(--color-manila-light); - --docsearch-key-pressed-shadow: inset 0 calc(var(--spacing) / 2) var(--spacing) - color-mix(in srgb, var(--color-black) 40%, transparent); - --docsearch-footer-background: var(--color-soot); - --docsearch-footer-shadow: 0 -1px 0 0 var(--color-warm-gray); /* 1px hairline */ - --docsearch-logo-color: currentColor; - --docsearch-search-button-background: transparent; - --docsearch-search-button-text-color: var(--color-manila-light); - } +/* ── Dark theme ── */ +.dark { + --docsearch-text-color: var(--color-manila-light); + --docsearch-secondary-text-color: var(--color-manila-dark); + --docsearch-background-color: var(--color-warm-gray); + --docsearch-modal-background: var(--color-faded-black); + --docsearch-modal-shadow: + inset 1px 1px 0 0 var(--color-warm-gray), + 0 calc(var(--spacing) * 0.75) calc(var(--spacing) * 2) color-mix(in srgb, var(--color-black) 50%, transparent); + --docsearch-searchbox-background: var(--color-soot); + --docsearch-searchbox-focus-background: var(--color-soot); + --docsearch-hit-background: var(--color-warm-gray); + --docsearch-hit-color: var(--color-manila-light); + --docsearch-hit-highlight-color: color-mix(in srgb, var(--color-gold) 10%, var(--color-warm-gray)); + --docsearch-highlight-color: var(--color-gold); + --docsearch-soft-primary-color: color-mix(in srgb, var(--color-gold) 10%, transparent); + --docsearch-focus-color: var(--color-gold); + --docsearch-subtle-color: var(--color-warm-gray); + --docsearch-muted-color: var(--color-manila-dark); + --docsearch-icon-color: var(--color-manila-dark); + --docsearch-container-background: color-mix(in srgb, var(--color-faded-black) 60%, transparent); + --docsearch-key-background: var(--color-soot); + --docsearch-key-color: var(--color-manila-light); + --docsearch-key-pressed-shadow: inset 0 calc(var(--spacing) / 2) var(--spacing) + color-mix(in srgb, var(--color-black) 40%, transparent); + --docsearch-footer-background: var(--color-soot); + --docsearch-footer-shadow: 0 -1px 0 0 var(--color-warm-gray); /* 1px hairline */ + --docsearch-logo-color: currentColor; + --docsearch-search-button-background: transparent; + --docsearch-search-button-text-color: var(--color-manila-light); +} - /* ── Trigger button — match rounded-pill navbar style ── */ - .DocSearch-Button { - box-sizing: border-box; - border-radius: 9999px; - border: none; - padding: 0 calc(var(--spacing) * 2); - gap: calc(var(--spacing) * 2); - height: auto; - min-height: calc(var(--spacing) * 6); - margin: 0; - font-family: inherit; - } +/* ── Trigger button — match rounded-pill navbar style ── */ +.DocSearch-Button { + box-sizing: border-box; + border-radius: 9999px; + border: none; + padding: 0 calc(var(--spacing) * 2); + gap: calc(var(--spacing) * 2); + height: auto; + min-height: calc(var(--spacing) * 6); + margin: 0; + font-family: inherit; +} - .DocSearch-Button:hover, - .DocSearch-Button:focus-within { - box-shadow: none; - } +.DocSearch-Button:hover, +.DocSearch-Button:focus-within { + box-shadow: none; +} - .DocSearch-Button .DocSearch-Search-Icon { - width: calc(var(--spacing) * 4); - height: calc(var(--spacing) * 4); - } +.DocSearch-Button .DocSearch-Search-Icon { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); +} - .DocSearch-Search-Icon circle, - .DocSearch-Search-Icon path { - stroke-width: var(--docsearch-icon-stroke-width); - } +.DocSearch-Search-Icon circle, +.DocSearch-Search-Icon path { + stroke-width: var(--docsearch-icon-stroke-width); +} - .DocSearch-Button .DocSearch-Search-Icon circle, - .DocSearch-Button .DocSearch-Search-Icon path { - stroke-width: calc(var(--docsearch-icon-stroke-width) * 2); - } +.DocSearch-Button .DocSearch-Search-Icon circle, +.DocSearch-Button .DocSearch-Search-Icon path { + stroke-width: calc(var(--docsearch-icon-stroke-width) * 2); +} - /* Hide button label text on small screens, keep icon + keyboard shortcut */ - .DocSearch-Button-Placeholder { - display: none; - } +/* Hide button label text on small screens, keep icon + keyboard shortcut */ +.DocSearch-Button-Placeholder { + display: none; +} - .DocSearch-Button-Keys { - display: none; - min-width: 0; - gap: 0; - } +.DocSearch-Button-Keys { + display: none; + min-width: 0; + gap: 0; +} - .DocSearch-Button .DocSearch-Button-Key { - background: none; - border: none; - box-shadow: none !important; - color: inherit; - width: auto; - height: auto; - font-family: var(--font-display); - font-size: var(--text-h4); - line-height: var(--text-h4--line-height); - letter-spacing: var(--text-h4--letter-spacing); - font-weight: var(--font-weight-bold); - padding: 0; - margin: 0; - } +.DocSearch-Button .DocSearch-Button-Key { + background: none; + border: none; + box-shadow: none !important; + color: inherit; + width: auto; + height: auto; + font-family: var(--font-display); + font-size: var(--text-h4); + line-height: var(--text-h4--line-height); + letter-spacing: var(--text-h4--letter-spacing); + font-weight: var(--font-weight-bold); + padding: 0; + margin: 0; +} - @media (width >= 40rem) { - .DocSearch-Button { - border: 1px solid var(--color-faded-black); - min-width: calc(var(--spacing) * 16.5); - } - - .dark .DocSearch-Button { - border-color: var(--color-manila-dark); - } - - .DocSearch-Button:hover, - .DocSearch-Button:focus-within { - border-color: var(--color-manila-dark); - } - - .dark .DocSearch-Button:hover, - .dark .DocSearch-Button:focus-within { - border-color: var(--color-warm-gray); - } - - .DocSearch-Button-Keys { - display: flex; - } - - .DocSearch-Button .DocSearch-Search-Icon { - width: calc(var(--spacing) * 3); - height: calc(var(--spacing) * 3); - } +@media (width >= 40rem) { + .DocSearch-Button { + border: 1px solid var(--color-faded-black); + min-width: calc(var(--spacing) * 16.5); } - /* ── Modal backdrop ── */ - .DocSearch-Container { - backdrop-filter: blur(calc(var(--spacing) / 2)); + .dark .DocSearch-Button { + border-color: var(--color-manila-dark); } - /* ── Consistent border-radius — DocSearch hardcodes 4px on these ── */ - .DocSearch-Modal { - border-radius: var(--radius-xs); + .DocSearch-Button:hover, + .DocSearch-Button:focus-within { + border-color: var(--color-manila-dark); } - .DocSearch-Form { - border-radius: var(--radius-xs) var(--radius-xs) 0 0; + .dark .DocSearch-Button:hover, + .dark .DocSearch-Button:focus-within { + border-color: var(--color-warm-gray); } - .DocSearch-Footer { - border-radius: 0 0 var(--radius-xs) var(--radius-xs); - border-block-start: none; + .DocSearch-Button-Keys { + display: flex; } - .DocSearch-Dropdown + .DocSearch-Footer { - border-block-start: 1px solid var(--docsearch-subtle-color); + .DocSearch-Button .DocSearch-Search-Icon { + width: calc(var(--spacing) * 3); + height: calc(var(--spacing) * 3); } +} - .DocSearch-Hit a { - border-radius: var(--radius-xs); - } +/* ── Modal backdrop ── */ +.DocSearch-Container { + backdrop-filter: blur(calc(var(--spacing) / 2)); +} - /* ── Typography — full text-token treatment (size + leading + tracking + weight) ── */ - .DocSearch-Modal { - font-family: var(--font-sans); - font-size: var(--text-p2); - line-height: var(--text-p2--line-height); - letter-spacing: var(--text-p2--letter-spacing); - font-weight: var(--text-p2--font-weight); - text-underline-offset: 0.25em; - } +/* ── Consistent border-radius — DocSearch hardcodes 4px on these ── */ +.DocSearch-Modal { + border-radius: var(--radius-xs); +} - .DocSearch-Input { - letter-spacing: var(--tracking-tight); - font-weight: var(--text-p1--font-weight); - } +.DocSearch-Form { + border-radius: var(--radius-xs) var(--radius-xs) 0 0; +} - .DocSearch-Button-Key, - .DocSearch-Escape-Key { - font-family: var(--font-mono); - font-size: var(--text-code); - line-height: var(--text-code--line-height); - letter-spacing: var(--text-code--letter-spacing); - font-weight: var(--text-code--font-weight); - font-variant-ligatures: none; - text-underline-offset: 0.125em; - } +.DocSearch-Footer { + border-radius: 0 0 var(--radius-xs) var(--radius-xs); + border-block-start: none; +} - .DocSearch-Hit-source { - font-size: var(--text-p2); - line-height: var(--text-p2--line-height); - letter-spacing: var(--text-p2--letter-spacing); - font-weight: var(--font-weight-bold); - padding-block-start: calc(var(--spacing) * 4); - padding-block-end: calc(var(--spacing) * 2); - } +.DocSearch-Dropdown + .DocSearch-Footer { + border-block-start: 1px solid var(--docsearch-subtle-color); +} - .DocSearch-Hit-title { - font-size: var(--text-p2); - line-height: var(--text-p2--line-height); - letter-spacing: var(--text-p2--letter-spacing); - } +.DocSearch-Hit a { + border-radius: var(--radius-xs); +} - .DocSearch-Hit-path { - font-size: var(--text-p3); - line-height: var(--text-p3--line-height); - letter-spacing: var(--text-p3--letter-spacing); - font-weight: var(--text-p3--font-weight); - } +/* ── Typography — full text-token treatment (size + leading + tracking + weight) ── */ +.DocSearch-Modal { + font-family: var(--font-sans); + font-size: var(--text-p2); + line-height: var(--text-p2--line-height); + letter-spacing: var(--text-p2--letter-spacing); + font-weight: var(--text-p2--font-weight); + text-underline-offset: 0.25em; +} - .DocSearch-Label, - .DocSearch-Help, - .DocSearch-NoResults-Help { - font-size: var(--text-p3); - line-height: var(--text-p3--line-height); - letter-spacing: var(--text-p3--letter-spacing); - font-weight: var(--text-p3--font-weight); - } +.DocSearch-Input { + letter-spacing: var(--tracking-tight); + font-weight: var(--text-p1--font-weight); +} - .DocSearch-Clear { - font-size: var(--text-p3); - font-weight: var(--text-p3--font-weight); - } +.DocSearch-Button-Key, +.DocSearch-Escape-Key { + font-family: var(--font-mono); + font-size: var(--text-code); + line-height: var(--text-code--line-height); + letter-spacing: var(--text-code--letter-spacing); + font-weight: var(--text-code--font-weight); + font-variant-ligatures: none; + text-underline-offset: 0.125em; +} - .DocSearch-Title strong { - font-weight: var(--font-weight-semibold); - } +.DocSearch-Hit-source { + font-size: var(--text-p2); + line-height: var(--text-p2--line-height); + letter-spacing: var(--text-p2--letter-spacing); + font-weight: var(--font-weight-bold); + padding-block-start: calc(var(--spacing) * 4); + padding-block-end: calc(var(--spacing) * 2); +} - /* ── Hit spacing — more breathing room, tighter title/path gap ── */ - .DocSearch-Hit-content-wrapper { - gap: 0; - } +.DocSearch-Hit-title { + font-size: var(--text-p2); + line-height: var(--text-p2--line-height); + letter-spacing: var(--text-p2--letter-spacing); +} - /* ── Algolia logo — SVG paths hardcode blue fills via .cls-1/.cls-2 ── */ - .DocSearch-Logo svg .cls-1, - .DocSearch-Logo svg .cls-2 { - fill: var(--docsearch-logo-color); - } +.DocSearch-Hit-path { + font-size: var(--text-p3); + line-height: var(--text-p3--line-height); + letter-spacing: var(--text-p3--letter-spacing); + font-weight: var(--text-p3--font-weight); +} - /* ── Text match highlight — keep readable in all states ── */ - .DocSearch-Hit mark, - .DocSearch-Hit[aria-selected="true"] mark { - text-decoration-color: var(--color-gold); - color: currentColor; - } +.DocSearch-Label, +.DocSearch-Help, +.DocSearch-NoResults-Help { + font-size: var(--text-p3); + line-height: var(--text-p3--line-height); + letter-spacing: var(--text-p3--letter-spacing); + font-weight: var(--text-p3--font-weight); +} + +.DocSearch-Clear { + font-size: var(--text-p3); + font-weight: var(--text-p3--font-weight); +} + +.DocSearch-Title strong { + font-weight: var(--font-weight-semibold); +} + +/* ── Hit spacing — more breathing room, tighter title/path gap ── */ +.DocSearch-Hit-content-wrapper { + gap: 0; +} + +/* ── Algolia logo — SVG paths hardcode blue fills via .cls-1/.cls-2 ── */ +.DocSearch-Logo svg .cls-1, +.DocSearch-Logo svg .cls-2 { + fill: var(--docsearch-logo-color); +} + +/* ── Text match highlight — keep readable in all states ── */ +.DocSearch-Hit mark, +.DocSearch-Hit[aria-selected="true"] mark { + text-decoration-color: var(--color-gold); + color: currentColor; } From 51a67a38fc6174e877fe4040ae27b2d48e43a34c Mon Sep 17 00:00:00 2001 From: Darius Cepulis Date: Fri, 20 Mar 2026 10:20:13 -0500 Subject: [PATCH 6/6] fix(site): set fixed DocSearch button width based on platform key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use fixed widths instead of min-width so the button size matches the displayed shortcut key — wider for "Ctrl K" (non-Apple) than "⌘K". Co-Authored-By: Claude Opus 4.6 (1M context) --- site/src/styles/docsearch.css | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/site/src/styles/docsearch.css b/site/src/styles/docsearch.css index f6ecde380..971893dd9 100644 --- a/site/src/styles/docsearch.css +++ b/site/src/styles/docsearch.css @@ -116,6 +116,10 @@ gap: 0; } +.DocSearch-Button-Keys:has(.DocSearch-Button-Key--ctrl) { + gap: var(--spacing); +} + .DocSearch-Button .DocSearch-Button-Key { background: none; border: none; @@ -135,7 +139,11 @@ @media (width >= 40rem) { .DocSearch-Button { border: 1px solid var(--color-faded-black); - min-width: calc(var(--spacing) * 16.5); + width: calc(var(--spacing) * 16.5); + } + + .DocSearch-Button:has(.DocSearch-Button-Key--ctrl) { + width: calc(var(--spacing) * 25); } .dark .DocSearch-Button {