Skip to content

Commit 1ca79f4

Browse files
authored
Improve lighthouse score (#1505)
1 parent 3cd8cff commit 1ca79f4

49 files changed

Lines changed: 1557 additions & 950 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docusaurus.config.ts

Lines changed: 13 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import type * as Preset from '@docusaurus/preset-classic';
22
import type { Config } from '@docusaurus/types';
33

4+
import clientModules from './src/plugins/client-modules.ts';
5+
import codeSplitVideoCss from './src/plugins/code-split-video-css.ts';
46
import disableFullySpecified from './src/plugins/disable-fully-specified.ts';
7+
import googleAnalyticsDeferred from './src/plugins/google-analytics-deferred.ts';
58
import latestAnnouncement from './src/plugins/latest-announcement.ts';
69
import llmsTxt from './src/plugins/llms-txt.ts';
710
import ogImage from './src/plugins/og-image.ts';
811
import reactNavigationVersions from './src/plugins/react-navigation-versions.ts';
912
import rehypeCodeblockMeta from './src/plugins/rehype-codeblock-meta.ts';
10-
import rehypeVideoAspectRatio from './src/plugins/rehype-video-aspect-ratio.ts';
13+
import rehypeMediaAttributes from './src/plugins/rehype-media-attributes.ts';
1114
import remarkNpm2Yarn from './src/plugins/remark-npm2yarn.ts';
1215
import remarkStaticToDynamic from './src/plugins/remark-static-to-dynamic.ts';
1316
import darkTheme from './src/themes/react-navigation-dark';
@@ -20,7 +23,7 @@ const config: Config = {
2023
tagline: 'Routing and navigation for your React Native apps',
2124
url: process.env.URL || 'https://reactnavigation.org',
2225
baseUrl: '/',
23-
favicon: 'img/favicon.ico',
26+
favicon: 'img/spiro.svg',
2427
organizationName: 'react-navigation',
2528
projectName: 'react-navigation.github.io',
2629
onBrokenLinks: 'throw',
@@ -44,17 +47,7 @@ const config: Config = {
4447
prism: {
4548
theme: lightTheme,
4649
darkTheme: darkTheme,
47-
additionalLanguages: [
48-
'bash',
49-
'json',
50-
'java',
51-
'kotlin',
52-
'groovy',
53-
'ruby',
54-
'swift',
55-
'objectivec',
56-
'toml',
57-
],
50+
additionalLanguages: ['bash', 'java', 'toml'],
5851
magicComments: [
5952
{
6053
className: 'theme-code-block-highlighted-line',
@@ -75,6 +68,8 @@ const config: Config = {
7568
logo: {
7669
alt: 'React Navigation Logo',
7770
src: 'img/spiro.svg',
71+
width: 294,
72+
height: 300,
7873
},
7974
items: [
8075
{
@@ -139,6 +134,9 @@ const config: Config = {
139134
},
140135
} satisfies Preset.ThemeConfig,
141136
plugins: [
137+
codeSplitVideoCss,
138+
clientModules,
139+
googleAnalyticsDeferred,
142140
disableFullySpecified,
143141
reactNavigationVersions,
144142
[llmsTxt, { latestVersion }],
@@ -189,7 +187,7 @@ const config: Config = {
189187
rehypeCodeblockMeta,
190188
{ match: { snack: true, lang: true, tabs: true } },
191189
],
192-
[rehypeVideoAspectRatio, { staticDir: 'static' }],
190+
[rehypeMediaAttributes, { staticDir: 'static' }],
193191
],
194192
},
195193
blog: {
@@ -202,6 +200,7 @@ const config: Config = {
202200
rehypeCodeblockMeta,
203201
{ match: { snack: true, lang: true, tabs: true } },
204202
],
203+
[rehypeMediaAttributes, { staticDir: 'static' }],
205204
],
206205
},
207206
pages: {
@@ -210,34 +209,9 @@ const config: Config = {
210209
theme: {
211210
customCss: './src/css/custom.css',
212211
},
213-
googleAnalytics: {
214-
trackingID: 'UA-10128745-16',
215-
},
216212
} satisfies Preset.Options,
217213
],
218214
],
219-
headTags: [
220-
{
221-
tagName: 'link',
222-
attributes: {
223-
rel: 'preconnect',
224-
href: 'https://fonts.googleapis.com',
225-
},
226-
},
227-
{
228-
tagName: 'link',
229-
attributes: {
230-
rel: 'preconnect',
231-
href: 'https://fonts.gstatic.com',
232-
crossOrigin: 'true',
233-
},
234-
},
235-
],
236-
scripts: [
237-
'/js/snack-helpers.js',
238-
'/js/toc-fixes.js',
239-
'/js/video-playback.js',
240-
],
241215
};
242216

243217
export default config;

package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@
1818
"fetch-sponsors": "node scripts/fetch-sponsors.js && prettier --write src/data/sponsors.js"
1919
},
2020
"dependencies": {
21-
"@docusaurus/core": "3.10.1",
22-
"@docusaurus/faster": "^3.10.1",
23-
"@docusaurus/plugin-client-redirects": "3.10.1",
24-
"@docusaurus/plugin-google-analytics": "3.10.1",
25-
"@docusaurus/preset-classic": "3.10.1",
26-
"@docusaurus/remark-plugin-npm2yarn": "3.10.1",
21+
"@docusaurus/core": "3.10.2",
22+
"@docusaurus/faster": "^3.10.2",
23+
"@docusaurus/plugin-client-redirects": "3.10.2",
24+
"@docusaurus/plugin-google-analytics": "3.10.2",
25+
"@docusaurus/preset-classic": "3.10.2",
26+
"@docusaurus/remark-plugin-npm2yarn": "3.10.2",
2727
"@mdx-js/react": "^3.1.1",
2828
"@octokit/graphql": "^9.0.3",
2929
"@react-navigation/core": "^7.13.5",
@@ -40,7 +40,7 @@
4040
},
4141
"devDependencies": {
4242
"@babel/types": "^7.28.5",
43-
"@docusaurus/tsconfig": "^3.10.1",
43+
"@docusaurus/tsconfig": "^3.10.2",
4444
"@ffprobe-installer/ffprobe": "^2.1.2",
4545
"@types/node": "^25.2.3",
4646
"@types/react": "^19.2.8",

src/client/index.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { ClientModule } from '@docusaurus/types';
2+
3+
let helpers:
4+
| Promise<
5+
[
6+
typeof import('./snack-helpers.ts'),
7+
typeof import('./toc-fixes.ts'),
8+
typeof import('./video-playback.ts'),
9+
]
10+
>
11+
| undefined;
12+
13+
const loadHelpers = () => {
14+
helpers ??= Promise.all([
15+
import('./snack-helpers.ts'),
16+
import('./toc-fixes.ts'),
17+
import('./video-playback.ts'),
18+
]);
19+
20+
return helpers;
21+
};
22+
23+
const clientModule: ClientModule = {
24+
onRouteDidUpdate() {
25+
void loadHelpers().then(([, { syncToc }]) => {
26+
syncToc();
27+
});
28+
},
29+
};
30+
31+
export default clientModule;
Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,18 @@ const DEPS_VERSIONS = {
6767
],
6868
};
6969

70+
type MajorVersion = keyof typeof DEPS_VERSIONS;
71+
72+
type SnackOptions = {
73+
code: string;
74+
label: string;
75+
templateId: string | null;
76+
};
77+
78+
function isMajorVersion(version: number): version is MajorVersion {
79+
return Object.hasOwn(DEPS_VERSIONS, version);
80+
}
81+
7082
function getVersion() {
7183
const maybeVersion = window.location.pathname.split('/')[1];
7284

@@ -77,13 +89,17 @@ function getVersion() {
7789
return '6.x';
7890
}
7991

80-
function getSnackUrl(options) {
92+
function getSnackUrl(options: SnackOptions) {
8193
let currentVersion = getVersion();
8294
let label = options.label || document.title;
8395
let code = options.code;
8496
let templateId = options.templateId;
8597

86-
const currentMajorVersion = currentVersion.match(/(\d+)\./)[1];
98+
const currentMajorVersion = Number(currentVersion.match(/(\d+)\./)?.[1]);
99+
100+
if (!isMajorVersion(currentMajorVersion)) {
101+
throw new Error(`Unsupported React Navigation version: ${currentVersion}`);
102+
}
87103

88104
let baseUrl =
89105
`https://snack.expo.io?platform=${DEFAULT_PLATFORM}&name=` +
@@ -104,14 +120,17 @@ function getSnackUrl(options) {
104120
}
105121
}
106122

107-
function findNearestCodeBlock(node) {
123+
function findNearestCodeBlock(node: HTMLElement) {
108124
let nextElement = node.nextElementSibling;
109-
if (!nextElement && node.parentElement.tagName === 'P') {
110-
nextElement = node.parentElement.nextElementSibling;
125+
const parent = node.parentElement;
126+
127+
if (!nextElement && parent?.tagName === 'P') {
128+
nextElement = parent.nextElementSibling;
111129
}
112130

113131
while (nextElement) {
114132
if (
133+
nextElement instanceof HTMLElement &&
115134
nextElement.tagName === 'DIV' &&
116135
(nextElement.className.includes('mdxCodeBlock') ||
117136
nextElement.className.includes('codeBlockContainer'))
@@ -127,7 +146,7 @@ let openIcon =
127146
'<svg width="14px" height="14px" viewBox="0 0 16 16" style="vertical-align: -1px"><g stroke="none" stroke-width="1" fill="none"><polyline stroke="currentColor" points="8.5 0.5 15.5 0.5 15.5 7.5"></polyline><path d="M8,8 L15.0710678,0.928932188" stroke="currentColor"></path><polyline stroke="currentColor" points="9.06944444 3.5 1.5 3.5 1.5 14.5 12.5 14.5 12.5 6.93055556"></polyline></g></svg>';
128147

129148
function appendSnackLink() {
130-
let samples = document.querySelectorAll('samp');
149+
let samples = document.querySelectorAll<HTMLElement>('samp');
131150

132151
if (!samples.length) {
133152
return;
@@ -151,7 +170,7 @@ function appendSnackLink() {
151170

152171
let link = document.createElement('a');
153172
link.className = 'code-block-snack-link';
154-
link.dataset.snack = true;
173+
link.dataset.snack = 'true';
155174
link.target = '_blank';
156175

157176
if (label) {
@@ -188,7 +207,17 @@ function initializeSnackObservers() {
188207
appendSnackLink();
189208

190209
const mutationObserver = new MutationObserver((mutations) => {
191-
mutations.forEach(appendSnackLink);
210+
if (
211+
mutations.some((mutation) =>
212+
Array.from(mutation.addedNodes).some(
213+
(node) =>
214+
node instanceof Element &&
215+
(node.matches('samp') || node.querySelector('samp'))
216+
)
217+
)
218+
) {
219+
appendSnackLink();
220+
}
192221
});
193222

194223
mutationObserver.observe(document.documentElement, {
@@ -197,4 +226,6 @@ function initializeSnackObservers() {
197226
});
198227
}
199228

200-
document.addEventListener('DOMContentLoaded', initializeSnackObservers);
229+
initializeSnackObservers();
230+
231+
export {};

src/client/toc-fixes.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* Docusaurus shows items in hidden tabs in the TOC.
3+
* It's confusing since it doesn't represent the actual page.
4+
* And clicking on those items doesn't do anything.
5+
* This scripts syncs the TOCs with visible headings.
6+
*/
7+
export const syncToc = () => {
8+
const headings = document.querySelectorAll<HTMLElement>(
9+
'article :is(h2, h3)'
10+
);
11+
12+
// Get all visible headings
13+
const titles = Array.from(headings)
14+
.filter((el) => el.offsetParent)
15+
.map((el) => trim(el.textContent ?? ''));
16+
17+
const toc = document.querySelectorAll<HTMLAnchorElement>(
18+
'.table-of-contents li > a'
19+
);
20+
21+
// Hide TOC items that don't have a corresponding heading
22+
toc.forEach((el) => {
23+
const item = el.parentElement;
24+
25+
if (!item) {
26+
return;
27+
}
28+
29+
if (!titles.includes(trim(el.textContent ?? ''))) {
30+
item.style.display = 'none';
31+
} else {
32+
item.style.display = 'block';
33+
}
34+
});
35+
};
36+
37+
const trim = (str: string) => str.trim().replace(/[\u200B-\u200D\uFEFF]/g, '');
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
.player {
2+
position: relative;
3+
display: inline-block;
4+
border-radius: var(--ifm-global-radius);
5+
6+
&.paused .control::after {
7+
background-image: url("data:image/svg+xml,<svg viewBox='0 0 18 18' xmlns='http://www.w3.org/2000/svg'><polygon points='6.5,4 6.5,14 14.5,9' fill='white'/></svg>");
8+
}
9+
10+
&:hover .control,
11+
&.paused .control {
12+
opacity: 1;
13+
}
14+
}
15+
16+
.control {
17+
display: flex;
18+
align-items: center;
19+
justify-content: center;
20+
position: absolute;
21+
top: 16px;
22+
right: 16px;
23+
width: 42px;
24+
height: 42px;
25+
background: rgba(0, 0, 0, 0.5);
26+
backdrop-filter: blur(8px);
27+
transition: 200ms opacity ease;
28+
opacity: 0;
29+
border-radius: 50%;
30+
border: 0;
31+
padding: 0;
32+
color: inherit;
33+
cursor: pointer;
34+
35+
&:focus-visible {
36+
opacity: 1;
37+
outline: 2px solid var(--ifm-color-primary);
38+
outline-offset: 2px;
39+
}
40+
41+
&::after {
42+
content: '';
43+
height: 24px;
44+
aspect-ratio: 1 / 1;
45+
background-image: url("data:image/svg+xml,<svg viewBox='0 0 18 18' xmlns='http://www.w3.org/2000/svg'><rect x='5' y='4' width='2.5' height='10' rx='0.5' fill='white'/><rect x='10.5' y='4' width='2.5' height='10' rx='0.5' fill='white'/></svg>");
46+
}
47+
}
48+
49+
@media (hover: none), (pointer: coarse) {
50+
.control {
51+
opacity: 1;
52+
}
53+
}

0 commit comments

Comments
 (0)