Skip to content

Commit 57406c3

Browse files
committed
Extract snippets to external JS files with syntax highlighting component
All 48 JavaScript snippets are extracted from inline MDX code blocks into standalone .js files under snippets/{category}/, making them individually addressable by LLMs and agents. - Add snippets/ directory with 48 .js files organized by category (CoreWebVitals, Interaction, Loading, Media, Resources) - Add components/Snippet.jsx: renders raw JS with highlight.js syntax highlighting, copy button, max-height scroll, and dark/light mode support - Add scripts/extract-snippets.js: one-time migration script that extracted code blocks from ### Snippet sections and rewrote MDX imports - Update all 46 MDX pages to import snippets via ?raw and use <Snippet> - Update next.config.js: webpack asset/source rule for ?raw imports, injected into oneOf to take precedence over SWC, with pre-loaders excluded to prevent React Fast Refresh injection - Update styles/globals.css: hljs light/dark themes, override Nextra's `pre code { padding: 0 !important }`
1 parent e9e5f4a commit 57406c3

94 files changed

Lines changed: 11782 additions & 11282 deletions

File tree

Some content is hidden

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

components/Snippet.jsx

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { useState, useMemo } from "react";
2+
import hljs from "highlight.js/lib/core";
3+
import javascript from "highlight.js/lib/languages/javascript";
4+
5+
hljs.registerLanguage("javascript", javascript);
6+
7+
export function Snippet({ code }) {
8+
const [copied, setCopied] = useState(false);
9+
const [hovered, setHovered] = useState(false);
10+
11+
const highlighted = useMemo(() => hljs.highlight(code, { language: "javascript" }).value, [code]);
12+
13+
async function handleCopy() {
14+
try {
15+
await navigator.clipboard.writeText(code);
16+
} catch {
17+
const el = document.createElement("textarea");
18+
el.value = code;
19+
el.style.cssText = "position:fixed;top:0;left:0;opacity:0";
20+
document.body.appendChild(el);
21+
el.select();
22+
document.execCommand("copy");
23+
document.body.removeChild(el);
24+
}
25+
setCopied(true);
26+
setTimeout(() => setCopied(false), 2000);
27+
}
28+
29+
return (
30+
<div
31+
className="nextra-code-block nx-relative nx-mt-6 first:nx-mt-0"
32+
onMouseEnter={() => setHovered(true)}
33+
onMouseLeave={() => setHovered(false)}
34+
>
35+
<pre
36+
data-language="js"
37+
data-theme="default"
38+
className="nx-bg-primary-700/5 nx-mb-4 nx-overflow-x-auto nx-rounded-xl nx-subpixel-antialiased dark:nx-bg-primary-300/10 nx-text-[13px] contrast-more:nx-border contrast-more:nx-border-primary-900/20 contrast-more:nx-contrast-150 contrast-more:dark:nx-border-primary-100/40"
39+
>
40+
<code
41+
data-language="js"
42+
data-theme="default"
43+
className="hljs nx-border-black nx-border-opacity-[0.04] nx-bg-opacity-[0.03] nx-bg-black nx-break-words nx-rounded-md nx-border dark:nx-border-white/10 dark:nx-bg-white/10 nx-py-4 nx-px-4"
44+
dangerouslySetInnerHTML={{ __html: highlighted }}
45+
/>
46+
</pre>
47+
<div
48+
className="nx-transition nx-flex nx-gap-1 nx-absolute nx-m-[11px] nx-right-0 nx-top-0"
49+
style={{ opacity: hovered || copied ? 1 : 0 }}
50+
>
51+
<button
52+
onClick={handleCopy}
53+
aria-label="Copy code"
54+
className="nextra-button nx-transition-all active:nx-opacity-50 nx-bg-primary-700/5 nx-border nx-border-black/5 nx-text-gray-600 hover:nx-text-gray-900 nx-rounded-md nx-p-1.5 dark:nx-bg-gray-100/5 dark:nx-border-white/10 dark:nx-text-gray-400 dark:hover:nx-text-gray-50"
55+
>
56+
{copied ? (
57+
<svg
58+
width="16"
59+
height="16"
60+
viewBox="0 0 24 24"
61+
fill="none"
62+
stroke="currentColor"
63+
strokeWidth="2"
64+
>
65+
<polyline points="20 6 9 17 4 12" />
66+
</svg>
67+
) : (
68+
<svg
69+
width="16"
70+
height="16"
71+
viewBox="0 0 24 24"
72+
fill="none"
73+
stroke="currentColor"
74+
strokeWidth="2"
75+
>
76+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
77+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
78+
</svg>
79+
)}
80+
</button>
81+
</div>
82+
</div>
83+
);
84+
}

next.config.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,36 @@
1+
const path = require('path')
12
const withNextra = require('nextra')({
23
theme: 'nextra-theme-docs',
34
themeConfig: './theme.config.jsx'
45
})
56

67
module.exports = withNextra({
8+
webpack(config) {
9+
const snippetsDir = path.join(__dirname, 'snippets')
10+
11+
// Exclude snippets from pre-loaders (React Fast Refresh, etc.)
12+
for (const rule of config.module.rules) {
13+
if (rule.enforce === 'pre') {
14+
rule.exclude = [].concat(rule.exclude || [], snippetsDir)
15+
}
16+
}
17+
18+
// Inject into oneOf so our rule takes precedence over the SWC loader
19+
const rawRule = {
20+
test: /\.js$/,
21+
include: snippetsDir,
22+
resourceQuery: /raw/,
23+
type: 'asset/source',
24+
}
25+
const oneOfRule = config.module.rules.find(r => Array.isArray(r.oneOf))
26+
if (oneOfRule) {
27+
oneOfRule.oneOf.unshift(rawRule)
28+
} else {
29+
config.module.rules.unshift(rawRule)
30+
}
31+
32+
return config
33+
},
734
async redirects() {
835
return [
936
{

pages/CoreWebVitals/CLS.mdx

Lines changed: 4 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import snippet from '../../snippets/CoreWebVitals/CLS.js?raw'
2+
import { Snippet } from '../../components/Snippet'
3+
14
# Cumulative Layout Shift (CLS)
25

36
### Overview
@@ -20,64 +23,7 @@ Unexpected layout shifts are frustrating and can cause users to click the wrong
2023
2124
### Snippet
2225

23-
```js copy
24-
// CLS Quick Check
25-
// https://webperf-snippets.nucliweb.net
26-
27-
(() => {
28-
let cls = 0;
29-
30-
const valueToRating = (score) =>
31-
score <= 0.1 ? "good" : score <= 0.25 ? "needs-improvement" : "poor";
32-
33-
const RATING = {
34-
good: { icon: "🟢", color: "#0CCE6A" },
35-
"needs-improvement": { icon: "🟡", color: "#FFA400" },
36-
poor: { icon: "🔴", color: "#FF4E42" },
37-
};
38-
39-
const logCLS = () => {
40-
const rating = valueToRating(cls);
41-
const { icon, color } = RATING[rating];
42-
console.log(
43-
`%cCLS: ${icon} ${cls.toFixed(4)} (${rating})`,
44-
`color: ${color}; font-weight: bold; font-size: 14px;`
45-
);
46-
};
47-
48-
const observer = new PerformanceObserver((list) => {
49-
for (const entry of list.getEntries()) {
50-
if (!entry.hadRecentInput) {
51-
cls += entry.value;
52-
}
53-
}
54-
logCLS();
55-
});
56-
57-
observer.observe({ type: "layout-shift", buffered: true });
58-
59-
// Update on visibility change (final CLS)
60-
document.addEventListener("visibilitychange", () => {
61-
if (document.visibilityState === "hidden") {
62-
observer.takeRecords();
63-
console.log("%c📊 Final CLS (on page hide):", "font-weight: bold;");
64-
logCLS();
65-
}
66-
});
67-
68-
// Expose function for manual check
69-
window.getCLS = () => {
70-
logCLS();
71-
return cls;
72-
};
73-
74-
console.log(
75-
" Call %cgetCLS()%c anytime to check current value.",
76-
"font-family: monospace; background: #f3f4f6; padding: 2px 4px;",
77-
""
78-
);
79-
})();
80-
```
26+
<Snippet code={snippet} />
8127

8228
### Understanding CLS
8329

0 commit comments

Comments
 (0)