From bbab641e91cdc123cf1a319a666c32f2d79a031d Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 7 Oct 2025 13:34:12 -0500 Subject: [PATCH 01/26] fix og:image links --- packages/dev/s2-docs/src/Layout.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/dev/s2-docs/src/Layout.tsx b/packages/dev/s2-docs/src/Layout.tsx index bd0df3ec68c..e1c20770343 100644 --- a/packages/dev/s2-docs/src/Layout.tsx +++ b/packages/dev/s2-docs/src/Layout.tsx @@ -61,6 +61,14 @@ const getTitle = (currentPage: Page): string => { const getOgImageUrl = (currentPage: Page): string => { const slug = currentPage.url.replace(/^\//, '').replace(/\.html$/, ''); + + if (slug.includes('s2-docs/')) { + // For build links, use the full URL + const ogPath = slug.replace(/s2-docs\//, 's2-docs/og/'); + return `https://reactspectrum.blob.core.windows.net/${ogPath}.png`; + } + + // For production, use relative path with /og/ prefix return `/og/${slug}.png`; }; From dd53fbc5c3d3847d3defb3dbde7a61731743b0a7 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 7 Oct 2025 13:34:40 -0500 Subject: [PATCH 02/26] fix font in SVG rendering --- packages/dev/s2-docs/scripts/generateOGImages.mjs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/dev/s2-docs/scripts/generateOGImages.mjs b/packages/dev/s2-docs/scripts/generateOGImages.mjs index 3d99587262b..8e190ee832f 100644 --- a/packages/dev/s2-docs/scripts/generateOGImages.mjs +++ b/packages/dev/s2-docs/scripts/generateOGImages.mjs @@ -119,9 +119,15 @@ async function getComponentSvg(title) { svgContent = svgContent.replace(/background:\s*#f4f6fc/g, 'background: #f8f8f8'); svgContent = svgContent.replace(/var\(--anatomy-font\)/g, 'adobe-clean'); - // Convert SVG to data URI for use as image source - const svgBase64 = Buffer.from(svgContent).toString('base64'); - return `data:image/svg+xml;base64,${svgBase64}`; + // Pre-render SVG to PNG to avoid issues with fonts not being available when SVG is used as img src + const pngBuffer = await sharp(Buffer.from(svgContent)) + .resize(340, 320, {fit: 'contain', background: {r: 248, g: 248, b: 248, alpha: 1}}) + .png() + .toBuffer(); + + // Convert PNG to data URI + const pngBase64 = pngBuffer.toString('base64'); + return `data:image/png;base64,${pngBase64}`; } catch (error) { console.warn(`Could not load SVG for ${title}: ${error.message}`); return null; From a837b0e7158ca81367729d15423e920b8d214512 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 7 Oct 2025 13:47:36 -0500 Subject: [PATCH 03/26] make logo dynamic --- .../dev/s2-docs/scripts/generateOGImages.mjs | 168 ++++++++++++++++-- 1 file changed, 149 insertions(+), 19 deletions(-) diff --git a/packages/dev/s2-docs/scripts/generateOGImages.mjs b/packages/dev/s2-docs/scripts/generateOGImages.mjs index 8e190ee832f..012fc29ba93 100644 --- a/packages/dev/s2-docs/scripts/generateOGImages.mjs +++ b/packages/dev/s2-docs/scripts/generateOGImages.mjs @@ -96,6 +96,152 @@ const componentSvgExceptions = { 'Drag and Drop': 'DragAndDrop.svg' }; +function getLibraryLogo(subtitle) { + if (subtitle === 'React Aria') { + return { + type: 'svg', + props: { + width: 156, + height: 138, + viewBox: '15.79 17.64 131.21 126.14', + xmlns: 'http://www.w3.org/2000/svg', + children: [ + { + type: 'defs', + props: { + children: { + type: 'clipPath', + props: { + id: 'a', + children: { + type: 'path', + props: { + d: 'M80 136c30.93 0 56-25.07 56-56s-25.07-56-56-56-56 25.07-56 56 25.07 56 56 56Zm8.48-86.3c0 4.69-3.8 8.48-8.48 8.48s-8.48-3.8-8.48-8.48 3.8-8.48 8.48-8.48 8.48 3.8 8.48 8.48ZM51.09 61.78c.52-1.8 2.39-2.85 4.2-2.33a89.865 89.865 0 0 0 48.82.17l.64-.18c1.81-.5 3.68.55 4.18 2.36a3.39 3.39 0 0 1-2.36 4.18l-.64.18a97.139 97.139 0 0 1-15.51 2.98c-1.03.11-1.82.97-1.82 2.01v14.89c0 .18.03.37.08.55 1.37 4.83 2.94 10.39 4.32 15.27.98 3.46 1.86 6.58 2.49 8.85.32 1.14.58 2.06.76 2.71l.04.15c.13.48.27.97.31 1.16a3.38 3.38 0 0 1-2.66 3.99 3.38 3.38 0 0 1-3.99-2.66c.02.09 0 .04-.08-.27-.04-.13-.09-.31-.15-.54-.18-.65-.44-1.57-.76-2.7-.64-2.27-1.52-5.39-2.49-8.85l-3.74-13.21a1.992 1.992 0 0 0-1.92-1.45h-1.6c-.89 0-1.68.59-1.92 1.45-1.22 4.3-2.55 9.01-3.74 13.21-.98 3.46-1.85 6.58-2.49 8.85-.32 1.13-.58 2.06-.76 2.7-.06.23-.11.41-.15.54-.09.31-.1.36-.08.27a3.39 3.39 0 1 1-6.65-1.33c.04-.18.18-.68.31-1.16l.04-.15c.18-.65.44-1.58.76-2.71.64-2.27 1.52-5.39 2.49-8.85 1.38-4.88 2.95-10.44 4.32-15.27.05-.18.08-.36.08-.55V71.16c0-1.04-.79-1.9-1.82-2.01a96.504 96.504 0 0 1-16.17-3.17 3.395 3.395 0 0 1-2.33-4.2Z', + fill: 'none' + } + } + } + } + } + }, + { + type: 'circle', + props: { + cx: 80, + cy: 80, + r: 50.83, + fill: '#fff' + } + }, + { + type: 'g', + props: { + clipPath: 'url(#a)', + children: { + type: 'path', + props: { + d: 'M15.79 17.64H147v126.14H15.79z', + fill: '#269ff4' + } + } + } + } + ] + } + }; + } else if (subtitle === 'Internationalized') { + return { + type: 'svg', + props: { + width: 156, + height: 138, + viewBox: '15 17 135 128', + xmlns: 'http://www.w3.org/2000/svg', + children: [ + { + type: 'defs', + props: { + children: [ + { + type: 'clipPath', + props: { + id: 'b', + children: { + type: 'path', + props: { + d: 'M23.98 79.99c0-17.01 7.58-32.25 19.54-42.52.92 2.02 2.32 4.67 4 7.13 1.05 1.54 2.28 3.1 3.65 4.41 1.33 1.27 3.04 2.55 5.08 3.13 3.29.94 7.1.8 9.98.7l.88-.03c1.63-.05 2.94-.07 4.03.05 1.11.11 1.57.33 1.71.42.31.2.39.37.41.43.03.08.07.22.03.46-.09.52-.53 1.28-1.4 1.76-2.07 1.15-4.14 2.6-5.69 4.59-1.63 2.09-2.54 4.6-2.54 7.55 0 .86-.07 1.4-.15 1.72v.03c-.28.06-.81.1-1.7.03-.27-.02-.55-.05-.86-.07-2.32-.21-5.69-.5-8.87-.17-3.54.37-8.1 1.64-10.56 5.96-1.08 1.89-2.33 5.04-2.03 8.8.31 3.92 2.27 7.99 6.71 11.55 5.12 4.1 10.75 4.98 18.12 5.8 4.2.47 6.58 1.37 7.94 2.33 1.19.84 1.85 1.9 2.18 3.55.58 2.88-.4 7.19-2.43 12.45-.97 2.52-2.11 5.05-3.24 7.52-.25.54-.5 1.08-.74 1.61-.81 1.76-1.61 3.47-2.27 5.03C41.73 127.9 24 106.03 24 80.02Zm26.36-46.91c-.07-.17-.15-.33-.24-.49a55.64 55.64 0 0 1 12.37-5.84c.05.3.14.6.26.89 2.06 4.9 8.3 10.94 16.68 10.94 4.33 0 7.8-1.86 10.35-3.97 2.53-2.1 4.34-4.6 5.47-6.33.41-.63.62-1.34.65-2.04.69.2 1.37.42 2.05.65-.06.18-.1.37-.13.57-.67 4.46-1.44 10.7-.74 16.34.69 5.54 3.01 11.91 9.76 14.25 5.36 1.86 10.66.94 14.8-.9 2.74-1.22 5.23-2.96 7.07-4.9 2.11 3.7 3.81 7.65 5.04 11.81l-1.75-.36c-3.01-.59-7.06-1.26-11.28-1.54-4.16-.28-8.77-.19-12.78.91-4.02 1.1-8.1 3.44-9.84 8.13-4.86 13.11 1.7 21.06 3.39 22.75 2.55 2.55 5.17 3.75 7.1 4.62.23.11.46.21.67.31 1.86.87 3.19 1.63 4.54 3.58.47.67.94 1.89 1.3 3.74.34 1.8.52 3.89.58 6.11.07 2.2.02 4.41-.04 6.43l-.06 1.63c-.04 1.08-.07 2.1-.09 2.95a55.775 55.775 0 0 1-35.48 12.67c-2.09 0-4.15-.11-6.18-.34.44-.97.91-1.99 1.41-3.08.26-.55.52-1.12.79-1.71 1.15-2.49 2.37-5.21 3.44-7.97 2.05-5.28 3.89-11.54 2.82-16.9-.67-3.35-2.3-6.32-5.41-8.51-2.94-2.07-6.85-3.21-11.67-3.74-7.46-.83-10.89-1.6-14.01-4.09-2.9-2.32-3.62-4.43-3.74-5.93-.13-1.67.44-3.21 1-4.2.51-.9 1.72-1.68 4.44-1.97 2.35-.25 4.9-.03 7.3.18.31.03.62.05.93.08 2.86.24 5.98-.1 8.18-2.42 2.06-2.18 2.33-5.13 2.33-7.33 0-1.17.32-1.95.86-2.64.61-.79 1.63-1.6 3.26-2.51 2.83-1.57 4.87-4.32 5.4-7.4.56-3.26-.64-6.73-3.89-8.9-1.72-1.14-3.68-1.55-5.33-1.72-1.68-.17-3.48-.13-5.1-.08-.22 0-.44.01-.65.02-3.22.11-5.75.19-7.75-.38-.27-.08-.86-.38-1.74-1.22-.84-.8-1.72-1.89-2.58-3.15-1.73-2.53-3.12-5.37-3.79-7Zm73.36 81.97a55.79 55.79 0 0 0 12.32-35.06c0-2.49-.16-4.94-.48-7.34-.05 0-.09-.02-.12-.02-.14-.03-.29-.06-.42-.09-.2-.04-.45-.1-.74-.16l-.35-.08c-.87-.2-2.06-.46-3.47-.73-2.84-.56-6.52-1.16-10.27-1.41-3.81-.25-7.38-.11-10.13.64-2.73.75-3.97 1.89-4.45 3.2-3.38 9.13 1.14 13.91 1.55 14.32 1.49 1.49 2.84 2.11 4.77 3 .23.11.47.22.73.34 2.42 1.13 5.22 2.64 7.73 6.27 1.39 2 2.14 4.47 2.58 6.79.46 2.38.65 4.94.72 7.37.03 1 .04 1.99.03 2.96Zm.28-69.78a56.276 56.276 0 0 0-18.47-15.18c-.57 4.1-.99 8.75-.5 12.73.56 4.56 2.1 6.86 4.44 7.68 2.96 1.03 6.11.6 8.92-.65 2.91-1.3 4.85-3.21 5.46-4.32.05-.09.1-.18.16-.26ZM80 23.96c2.75 0 5.46.2 8.11.58-.9 1.31-2.06 2.75-3.46 3.91-1.61 1.33-3.33 2.13-5.24 2.13-4.39 0-7.99-3.27-9.19-5.78 3.17-.56 6.44-.85 9.78-.85Z', + fill: 'none' + } + } + } + }, + { + type: 'clipPath', + props: { + id: 'c', + children: { + type: 'path', + props: { + d: 'M23.98 80c0-17.01 7.58-32.25 19.54-42.52.92 2.02 2.32 4.67 4 7.13 1.05 1.54 2.28 3.1 3.65 4.41 1.33 1.27 3.04 2.55 5.08 3.13 3.29.94 7.1.8 9.98.7l.88-.03c1.63-.05 2.94-.07 4.03.05 1.11.11 1.57.33 1.71.42.31.2.39.37.41.43.03.08.07.22.03.46-.09.52-.53 1.28-1.4 1.76-2.07 1.15-4.14 2.6-5.69 4.59-1.63 2.09-2.54 4.6-2.54 7.55 0 .86-.07 1.4-.15 1.72v.03c-.28.06-.81.1-1.7.03-.27-.02-.55-.05-.86-.07-2.32-.21-5.69-.5-8.87-.17-3.54.37-8.1 1.64-10.56 5.96-1.08 1.89-2.33 5.04-2.03 8.8.31 3.92 2.27 7.99 6.71 11.55 5.12 4.1 10.75 4.98 18.12 5.8 4.2.47 6.58 1.37 7.94 2.33 1.19.84 1.85 1.9 2.18 3.55.58 2.88-.4 7.19-2.43 12.45-.97 2.52-2.11 5.05-3.24 7.52-.25.54-.5 1.08-.74 1.61-.81 1.76-1.61 3.47-2.27 5.03C41.73 127.91 24 106.04 24 80.03Zm99.72 35.06A55.79 55.79 0 0 0 136.02 80c0-2.49-.16-4.94-.48-7.34-.05 0-.09-.02-.12-.02-.14-.03-.29-.06-.42-.09-.2-.04-.45-.1-.74-.16l-.35-.08c-.87-.2-2.06-.46-3.47-.73-2.84-.56-6.52-1.16-10.27-1.41-3.81-.25-7.38-.11-10.13.64-2.73.75-3.97 1.89-4.45 3.2-3.38 9.13 1.14 13.91 1.55 14.32 1.49 1.49 2.84 2.11 4.77 3 .23.11.47.22.73.34 2.42 1.13 5.22 2.64 7.73 6.27 1.39 2 2.14 4.47 2.58 6.79.46 2.38.65 4.94.72 7.37.03 1 .04 1.99.03 2.96Zm.28-69.77a56.276 56.276 0 0 0-18.47-15.18c-.57 4.1-.99 8.75-.5 12.73.56 4.56 2.1 6.86 4.44 7.68 2.96 1.03 6.11.6 8.92-.65 2.91-1.3 4.85-3.21 5.46-4.32.05-.09.1-.18.16-.26ZM80 23.98c2.75 0 5.46.2 8.11.58-.9 1.31-2.06 2.75-3.46 3.91-1.61 1.33-3.33 2.13-5.24 2.13-4.39 0-7.99-3.27-9.19-5.78 3.17-.56 6.44-.85 9.78-.85Z', + fill: 'none' + } + } + } + } + ] + } + }, + { + type: 'g', + props: { + clipPath: 'url(#b)', + children: { + type: 'path', + props: { + d: 'M15 17h135v128H15z', + fill: '#6995fe' + } + } + } + }, + { + type: 'g', + props: { + clipPath: 'url(#c)', + children: { + type: 'path', + props: { + d: 'M15 17.01h135v128H15z', + fill: '#099d59' + } + } + } + } + ] + } + }; + } else { + // Adobe logo for React Spectrum + return { + type: 'svg', + props: { + width: 156, + height: 138, + viewBox: '0 0 501.71 444.05', + xmlns: 'http://www.w3.org/2000/svg', + children: { + type: 'polygon', + props: { + fill: '#eb1000', + strokeWidth: 0, + points: '297.58 444.05 261.13 342.65 169.67 342.65 246.54 149.12 363.19 444.05 501.71 444.05 316.8 0 186.23 0 0 444.05 297.58 444.05 297.58 444.05' + } + } + } + }; + } +} + async function getComponentSvg(title) { // First try exception mappings let svgFileName = componentSvgExceptions[title]; @@ -203,7 +349,7 @@ for (let file of files) { } } }, - // Bottom section: Adobe logo + text + // Bottom section: Library logo + text { type: 'div', props: { @@ -216,24 +362,8 @@ for (let file of files) { gap: 40 }, children: [ - // Adobe logo - { - type: 'svg', - props: { - width: 156, - height: 138, - viewBox: '0 0 501.71 444.05', - xmlns: 'http://www.w3.org/2000/svg', - children: { - type: 'polygon', - props: { - fill: '#eb1000', - strokeWidth: 0, - points: '297.58 444.05 261.13 342.65 169.67 342.65 246.54 149.12 363.19 444.05 501.71 444.05 316.8 0 186.23 0 0 444.05 297.58 444.05 297.58 444.05' - } - } - } - }, + // Library logo (Adobe for React Spectrum, React Aria, or Internationalized) + getLibraryLogo(subtitle), // Text content { type: 'div', From c152f0172b2074bd81414dc5f62a33b68947047f Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 7 Oct 2025 13:53:05 -0500 Subject: [PATCH 04/26] more name aliases for illustrations --- packages/dev/s2-docs/scripts/generateOGImages.mjs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/dev/s2-docs/scripts/generateOGImages.mjs b/packages/dev/s2-docs/scripts/generateOGImages.mjs index 012fc29ba93..675521504ea 100644 --- a/packages/dev/s2-docs/scripts/generateOGImages.mjs +++ b/packages/dev/s2-docs/scripts/generateOGImages.mjs @@ -91,9 +91,15 @@ const [adobeCleanRegular, adobeCleanBold] = await Promise.all([ // Mappings for components that don't match their SVG file names const componentSvgExceptions = { + 'Accordion': 'DisclosureGroup.svg', + 'ActionButtonGroup': 'ButtonGroup.svg', // TODO: get better illustration + 'ActionMenu': 'Menu.svg', + 'Drag and Drop': 'DragAndDrop.svg', 'GridList': 'ListView.svg', + 'LinkButton': 'Button.svg', // TODO: get better illustration 'Select': 'Picker.svg', - 'Drag and Drop': 'DragAndDrop.svg' + 'TableView': 'Table.svg', + 'TreeView': 'Tree.svg' }; function getLibraryLogo(subtitle) { From eabdcba7da984afde36b44cb8dce57c4eae37237 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 7 Oct 2025 13:56:40 -0500 Subject: [PATCH 05/26] Revert "fix font in SVG rendering" This reverts commit dd53fbc5c3d3847d3defb3dbde7a61731743b0a7. --- packages/dev/s2-docs/scripts/generateOGImages.mjs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/dev/s2-docs/scripts/generateOGImages.mjs b/packages/dev/s2-docs/scripts/generateOGImages.mjs index 675521504ea..29e2fa08e41 100644 --- a/packages/dev/s2-docs/scripts/generateOGImages.mjs +++ b/packages/dev/s2-docs/scripts/generateOGImages.mjs @@ -271,15 +271,9 @@ async function getComponentSvg(title) { svgContent = svgContent.replace(/background:\s*#f4f6fc/g, 'background: #f8f8f8'); svgContent = svgContent.replace(/var\(--anatomy-font\)/g, 'adobe-clean'); - // Pre-render SVG to PNG to avoid issues with fonts not being available when SVG is used as img src - const pngBuffer = await sharp(Buffer.from(svgContent)) - .resize(340, 320, {fit: 'contain', background: {r: 248, g: 248, b: 248, alpha: 1}}) - .png() - .toBuffer(); - - // Convert PNG to data URI - const pngBase64 = pngBuffer.toString('base64'); - return `data:image/png;base64,${pngBase64}`; + // Convert SVG to data URI for use as image source + const svgBase64 = Buffer.from(svgContent).toString('base64'); + return `data:image/svg+xml;base64,${svgBase64}`; } catch (error) { console.warn(`Could not load SVG for ${title}: ${error.message}`); return null; From 8b3fa87dcf05f6a6086f581a7a713cba93e01197 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 7 Oct 2025 14:13:33 -0500 Subject: [PATCH 06/26] try inlining fonts in svg --- .../dev/s2-docs/scripts/generateOGImages.mjs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/dev/s2-docs/scripts/generateOGImages.mjs b/packages/dev/s2-docs/scripts/generateOGImages.mjs index 29e2fa08e41..94f22483d47 100644 --- a/packages/dev/s2-docs/scripts/generateOGImages.mjs +++ b/packages/dev/s2-docs/scripts/generateOGImages.mjs @@ -89,6 +89,22 @@ const [adobeCleanRegular, adobeCleanBold] = await Promise.all([ loadFont('https://use.typekit.net/af/eaf09c/000000000000000000017703/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3') ]); +// Detect font MIME type and CSS format for inlining +function detectFontFormatAndMime(bytes) { + const buf = Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes); + const header = buf.subarray(0, 4).toString('ascii'); + if (header === 'wOF2') { + return {mime: 'font/woff2', format: 'woff2'}; + } + if (header === 'wOFF') { + return {mime: 'font/woff', format: 'woff'}; + } + if (header === 'OTTO') { + return {mime: 'font/otf', format: 'opentype'}; + } + return {mime: 'font/woff2', format: 'woff2'}; +} + // Mappings for components that don't match their SVG file names const componentSvgExceptions = { 'Accordion': 'DisclosureGroup.svg', @@ -271,6 +287,14 @@ async function getComponentSvg(title) { svgContent = svgContent.replace(/background:\s*#f4f6fc/g, 'background: #f8f8f8'); svgContent = svgContent.replace(/var\(--anatomy-font\)/g, 'adobe-clean'); + // Inline fonts so text within illustration renders in CI + const regMeta = detectFontFormatAndMime(adobeCleanRegular); + const boldMeta = detectFontFormatAndMime(adobeCleanBold); + const regB64 = Buffer.from(adobeCleanRegular).toString('base64'); + const boldB64 = Buffer.from(adobeCleanBold).toString('base64'); + const fontCss = ``; + svgContent = svgContent.replace(/]*>/i, (m) => m + fontCss); + // Convert SVG to data URI for use as image source const svgBase64 = Buffer.from(svgContent).toString('base64'); return `data:image/svg+xml;base64,${svgBase64}`; From d23760b5f0f3e0b672d8e2ab3d30aae80e2f6bdc Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 7 Oct 2025 15:03:53 -0500 Subject: [PATCH 07/26] Revert "try inlining fonts in svg" This reverts commit 8b3fa87dcf05f6a6086f581a7a713cba93e01197. --- .../dev/s2-docs/scripts/generateOGImages.mjs | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/packages/dev/s2-docs/scripts/generateOGImages.mjs b/packages/dev/s2-docs/scripts/generateOGImages.mjs index 94f22483d47..29e2fa08e41 100644 --- a/packages/dev/s2-docs/scripts/generateOGImages.mjs +++ b/packages/dev/s2-docs/scripts/generateOGImages.mjs @@ -89,22 +89,6 @@ const [adobeCleanRegular, adobeCleanBold] = await Promise.all([ loadFont('https://use.typekit.net/af/eaf09c/000000000000000000017703/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3') ]); -// Detect font MIME type and CSS format for inlining -function detectFontFormatAndMime(bytes) { - const buf = Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes); - const header = buf.subarray(0, 4).toString('ascii'); - if (header === 'wOF2') { - return {mime: 'font/woff2', format: 'woff2'}; - } - if (header === 'wOFF') { - return {mime: 'font/woff', format: 'woff'}; - } - if (header === 'OTTO') { - return {mime: 'font/otf', format: 'opentype'}; - } - return {mime: 'font/woff2', format: 'woff2'}; -} - // Mappings for components that don't match their SVG file names const componentSvgExceptions = { 'Accordion': 'DisclosureGroup.svg', @@ -287,14 +271,6 @@ async function getComponentSvg(title) { svgContent = svgContent.replace(/background:\s*#f4f6fc/g, 'background: #f8f8f8'); svgContent = svgContent.replace(/var\(--anatomy-font\)/g, 'adobe-clean'); - // Inline fonts so text within illustration renders in CI - const regMeta = detectFontFormatAndMime(adobeCleanRegular); - const boldMeta = detectFontFormatAndMime(adobeCleanBold); - const regB64 = Buffer.from(adobeCleanRegular).toString('base64'); - const boldB64 = Buffer.from(adobeCleanBold).toString('base64'); - const fontCss = ``; - svgContent = svgContent.replace(/]*>/i, (m) => m + fontCss); - // Convert SVG to data URI for use as image source const svgBase64 = Buffer.from(svgContent).toString('base64'); return `data:image/svg+xml;base64,${svgBase64}`; From 9f785a202c0edfd9bae21583c0e8f626b843ec79 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 7 Oct 2025 17:24:02 -0500 Subject: [PATCH 08/26] generate images for index pages --- .../dev/s2-docs/scripts/generateOGImages.mjs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/packages/dev/s2-docs/scripts/generateOGImages.mjs b/packages/dev/s2-docs/scripts/generateOGImages.mjs index 29e2fa08e41..c7a3d5f41bc 100644 --- a/packages/dev/s2-docs/scripts/generateOGImages.mjs +++ b/packages/dev/s2-docs/scripts/generateOGImages.mjs @@ -291,6 +291,7 @@ for (let file of files) { .replace(/\\/g, '/') .replace(/\.mdx?$/, ''); let subtitle = getSubtitle(slug); + let isIndexPage = slug.includes('/index'); // Get component SVG if available const componentSvg = await getComponentSvg(title); @@ -299,6 +300,51 @@ for (let file of files) { } let svg = await satori( + isIndexPage ? + // Index page layout: Centered logo and library name + { + type: 'div', + props: { + style: { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + width: '100%', + height: '100%', + backgroundColor: '#ffffff', + fontFamily: 'adobe-clean', + color: '#000000' + }, + children: { + type: 'div', + props: { + style: { + display: 'flex', + alignItems: 'center', + gap: 40 + }, + children: [ + // Library logo + getLibraryLogo(subtitle), + // Library name + { + type: 'div', + props: { + style: { + fontSize: 84, + fontWeight: 700, + lineHeight: 1.1 + }, + children: subtitle + } + } + ] + } + } + } + } + : + // Component page layout: Component illustration + bottom section { type: 'div', props: { From 195a487e8e5442c689f770a1ad66058b219f36ea Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 7 Oct 2025 17:34:06 -0500 Subject: [PATCH 09/26] generate images for pages without illustrations --- .../dev/s2-docs/scripts/generateOGImages.mjs | 102 ++++++++++++++---- 1 file changed, 80 insertions(+), 22 deletions(-) diff --git a/packages/dev/s2-docs/scripts/generateOGImages.mjs b/packages/dev/s2-docs/scripts/generateOGImages.mjs index c7a3d5f41bc..69cacf57edd 100644 --- a/packages/dev/s2-docs/scripts/generateOGImages.mjs +++ b/packages/dev/s2-docs/scripts/generateOGImages.mjs @@ -291,18 +291,16 @@ for (let file of files) { .replace(/\\/g, '/') .replace(/\.mdx?$/, ''); let subtitle = getSubtitle(slug); - let isIndexPage = slug.includes('/index'); + let isIndexPage = slug === 'index' || slug.endsWith('/index'); // Get component SVG if available const componentSvg = await getComponentSvg(title); - if (!componentSvg) { - continue; - } - let svg = await satori( - isIndexPage ? + // Determine layout type + let layout; + if (isIndexPage) { // Index page layout: Centered logo and library name - { + layout = { type: 'div', props: { style: { @@ -342,10 +340,76 @@ for (let file of files) { } } } - } - : + }; + } else if (!componentSvg) { + // Page without illustration: Centered logo + page title + library name + layout = { + type: 'div', + props: { + style: { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + width: '100%', + height: '100%', + backgroundColor: '#ffffff', + fontFamily: 'adobe-clean', + color: '#000000' + }, + children: { + type: 'div', + props: { + style: { + display: 'flex', + alignItems: 'center', + gap: 40 + }, + children: [ + // Library logo + getLibraryLogo(subtitle), + // Text content + { + type: 'div', + props: { + style: { + display: 'flex', + flexDirection: 'column', + gap: 8 + }, + children: [ + { + type: 'div', + props: { + style: { + fontSize: 84, + fontWeight: 700, + lineHeight: 1.1 + }, + children: title + } + }, + { + type: 'div', + props: { + style: { + fontSize: 56, + fontWeight: 400, + color: '#464646' + }, + children: subtitle + } + } + ] + } + } + ] + } + } + } + }; + } else { // Component page layout: Component illustration + bottom section - { + layout = { type: 'div', props: { style: { @@ -372,7 +436,7 @@ for (let file of files) { padding: '40px', borderBottom: '1px solid #e5e5e5' }, - children: componentSvg ? { + children: { type: 'img', props: { src: componentSvg, @@ -382,16 +446,6 @@ for (let file of files) { objectFit: 'contain' } } - } : { - type: 'div', - props: { - style: { - fontSize: 72, - fontWeight: 400, - color: '#464646' - }, - children: title - } } } }, @@ -450,7 +504,11 @@ for (let file of files) { } ] } - }, + }; + } + + let svg = await satori( + layout, { width: 1200, height: 630, From 08ae097d5ead071420648143c15d338a20c0d475 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 7 Oct 2025 17:44:49 -0500 Subject: [PATCH 10/26] try installing fonts in CI --- .circleci/config.yml | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index b7665dbc8f5..fae3ec840a5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -454,6 +454,19 @@ jobs: - restore_cache: key: react-spectrum-{{ .Environment.CACHE_VERSION }}-{{ .Environment.CIRCLE_SHA1 }} + - run: + name: Install fonts + command: | + mkdir -p ~/.fonts + # Adobe Clean Spectrum VF + wget -O ~/.fonts/adobe-clean-spectrum-vf.woff2 "https://use.typekit.net/af/ca4cba/0000000000000000775c55a1/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n1&v=3" + # Adobe Clean Spectrum SRF VF + wget -O ~/.fonts/adobe-clean-spectrum-srf-vf.woff2 "https://use.typekit.net/af/4a1d0d/0000000000000000775c55a4/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n1&v=3" + # Source Code Pro + wget -O ~/.fonts/source-code-pro-400.woff2 "https://use.typekit.net/af/80f457/00000000000000007758ce1d/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n4&v=3" + wget -O ~/.fonts/source-code-pro-700.woff2 "https://use.typekit.net/af/88da4d/00000000000000007758ce1a/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n7&v=3" + fc-cache -f -v + - run: name: build docs command: make website @@ -469,6 +482,19 @@ jobs: - restore_cache: key: react-spectrum-{{ .Environment.CACHE_VERSION }}-{{ .Environment.CIRCLE_SHA1 }} + - run: + name: Install fonts + command: | + mkdir -p ~/.fonts + # Adobe Clean Spectrum VF + wget -O ~/.fonts/adobe-clean-spectrum-vf.woff2 "https://use.typekit.net/af/ca4cba/0000000000000000775c55a1/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n1&v=3" + # Adobe Clean Spectrum SRF VF + wget -O ~/.fonts/adobe-clean-spectrum-srf-vf.woff2 "https://use.typekit.net/af/4a1d0d/0000000000000000775c55a4/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n1&v=3" + # Source Code Pro + wget -O ~/.fonts/source-code-pro-400.woff2 "https://use.typekit.net/af/80f457/00000000000000007758ce1d/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n4&v=3" + wget -O ~/.fonts/source-code-pro-700.woff2 "https://use.typekit.net/af/88da4d/00000000000000007758ce1a/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n7&v=3" + fc-cache -f -v + - run: name: build docs command: make website-production @@ -484,6 +510,19 @@ jobs: - restore_cache: key: react-spectrum-{{ .Environment.CACHE_VERSION }}-{{ .Environment.CIRCLE_SHA1 }} + - run: + name: Install fonts + command: | + mkdir -p ~/.fonts + # Adobe Clean Spectrum VF + wget -O ~/.fonts/adobe-clean-spectrum-vf.woff2 "https://use.typekit.net/af/ca4cba/0000000000000000775c55a1/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n1&v=3" + # Adobe Clean Spectrum SRF VF + wget -O ~/.fonts/adobe-clean-spectrum-srf-vf.woff2 "https://use.typekit.net/af/4a1d0d/0000000000000000775c55a4/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n1&v=3" + # Source Code Pro + wget -O ~/.fonts/source-code-pro-400.woff2 "https://use.typekit.net/af/80f457/00000000000000007758ce1d/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n4&v=3" + wget -O ~/.fonts/source-code-pro-700.woff2 "https://use.typekit.net/af/88da4d/00000000000000007758ce1a/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n7&v=3" + fc-cache -f -v + - run: name: build s2 docs command: make s2-docs From c96e5c33b04e466ca7719a6e6ef29554f126f3b5 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 7 Oct 2025 17:54:37 -0500 Subject: [PATCH 11/26] switch fonts --- .circleci/config.yml | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fae3ec840a5..28f6362a661 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -458,14 +458,10 @@ jobs: name: Install fonts command: | mkdir -p ~/.fonts - # Adobe Clean Spectrum VF - wget -O ~/.fonts/adobe-clean-spectrum-vf.woff2 "https://use.typekit.net/af/ca4cba/0000000000000000775c55a1/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n1&v=3" - # Adobe Clean Spectrum SRF VF - wget -O ~/.fonts/adobe-clean-spectrum-srf-vf.woff2 "https://use.typekit.net/af/4a1d0d/0000000000000000775c55a4/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n1&v=3" - # Source Code Pro - wget -O ~/.fonts/source-code-pro-400.woff2 "https://use.typekit.net/af/80f457/00000000000000007758ce1d/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n4&v=3" - wget -O ~/.fonts/source-code-pro-700.woff2 "https://use.typekit.net/af/88da4d/00000000000000007758ce1a/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n7&v=3" - fc-cache -f -v + # Adobe Clean Regular (400) + wget -O ~/.fonts/adobe-clean-regular.woff "https://use.typekit.net/af/cb695f/000000000000000000017701/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3" + # Adobe Clean Bold (700) + wget -O ~/.fonts/adobe-clean-bold.woff "https://use.typekit.net/af/eaf09c/000000000000000000017703/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3" - run: name: build docs @@ -486,14 +482,10 @@ jobs: name: Install fonts command: | mkdir -p ~/.fonts - # Adobe Clean Spectrum VF - wget -O ~/.fonts/adobe-clean-spectrum-vf.woff2 "https://use.typekit.net/af/ca4cba/0000000000000000775c55a1/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n1&v=3" - # Adobe Clean Spectrum SRF VF - wget -O ~/.fonts/adobe-clean-spectrum-srf-vf.woff2 "https://use.typekit.net/af/4a1d0d/0000000000000000775c55a4/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n1&v=3" - # Source Code Pro - wget -O ~/.fonts/source-code-pro-400.woff2 "https://use.typekit.net/af/80f457/00000000000000007758ce1d/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n4&v=3" - wget -O ~/.fonts/source-code-pro-700.woff2 "https://use.typekit.net/af/88da4d/00000000000000007758ce1a/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n7&v=3" - fc-cache -f -v + # Adobe Clean Regular (400) + wget -O ~/.fonts/adobe-clean-regular.woff "https://use.typekit.net/af/cb695f/000000000000000000017701/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3" + # Adobe Clean Bold (700) + wget -O ~/.fonts/adobe-clean-bold.woff "https://use.typekit.net/af/eaf09c/000000000000000000017703/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3" - run: name: build docs @@ -514,14 +506,10 @@ jobs: name: Install fonts command: | mkdir -p ~/.fonts - # Adobe Clean Spectrum VF - wget -O ~/.fonts/adobe-clean-spectrum-vf.woff2 "https://use.typekit.net/af/ca4cba/0000000000000000775c55a1/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n1&v=3" - # Adobe Clean Spectrum SRF VF - wget -O ~/.fonts/adobe-clean-spectrum-srf-vf.woff2 "https://use.typekit.net/af/4a1d0d/0000000000000000775c55a4/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n1&v=3" - # Source Code Pro - wget -O ~/.fonts/source-code-pro-400.woff2 "https://use.typekit.net/af/80f457/00000000000000007758ce1d/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n4&v=3" - wget -O ~/.fonts/source-code-pro-700.woff2 "https://use.typekit.net/af/88da4d/00000000000000007758ce1a/31/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n7&v=3" - fc-cache -f -v + # Adobe Clean Regular (400) + wget -O ~/.fonts/adobe-clean-regular.woff "https://use.typekit.net/af/cb695f/000000000000000000017701/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3" + # Adobe Clean Bold (700) + wget -O ~/.fonts/adobe-clean-bold.woff "https://use.typekit.net/af/eaf09c/000000000000000000017703/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3" - run: name: build s2 docs From 64d772575308b5d97b4b1cb041a706b1a8882149 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 7 Oct 2025 18:11:11 -0500 Subject: [PATCH 12/26] remove font install from CI --- .circleci/config.yml | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 28f6362a661..b7665dbc8f5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -454,15 +454,6 @@ jobs: - restore_cache: key: react-spectrum-{{ .Environment.CACHE_VERSION }}-{{ .Environment.CIRCLE_SHA1 }} - - run: - name: Install fonts - command: | - mkdir -p ~/.fonts - # Adobe Clean Regular (400) - wget -O ~/.fonts/adobe-clean-regular.woff "https://use.typekit.net/af/cb695f/000000000000000000017701/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3" - # Adobe Clean Bold (700) - wget -O ~/.fonts/adobe-clean-bold.woff "https://use.typekit.net/af/eaf09c/000000000000000000017703/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3" - - run: name: build docs command: make website @@ -478,15 +469,6 @@ jobs: - restore_cache: key: react-spectrum-{{ .Environment.CACHE_VERSION }}-{{ .Environment.CIRCLE_SHA1 }} - - run: - name: Install fonts - command: | - mkdir -p ~/.fonts - # Adobe Clean Regular (400) - wget -O ~/.fonts/adobe-clean-regular.woff "https://use.typekit.net/af/cb695f/000000000000000000017701/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3" - # Adobe Clean Bold (700) - wget -O ~/.fonts/adobe-clean-bold.woff "https://use.typekit.net/af/eaf09c/000000000000000000017703/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3" - - run: name: build docs command: make website-production @@ -502,15 +484,6 @@ jobs: - restore_cache: key: react-spectrum-{{ .Environment.CACHE_VERSION }}-{{ .Environment.CIRCLE_SHA1 }} - - run: - name: Install fonts - command: | - mkdir -p ~/.fonts - # Adobe Clean Regular (400) - wget -O ~/.fonts/adobe-clean-regular.woff "https://use.typekit.net/af/cb695f/000000000000000000017701/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3" - # Adobe Clean Bold (700) - wget -O ~/.fonts/adobe-clean-bold.woff "https://use.typekit.net/af/eaf09c/000000000000000000017703/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3" - - run: name: build s2 docs command: make s2-docs From 5b6c0029d30ee4323d31053c1c31dace7be6a440 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 7 Oct 2025 18:18:14 -0500 Subject: [PATCH 13/26] try inlining font-face and pre-rendering illustration as png --- .../dev/s2-docs/scripts/generateOGImages.mjs | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/packages/dev/s2-docs/scripts/generateOGImages.mjs b/packages/dev/s2-docs/scripts/generateOGImages.mjs index 69cacf57edd..7c7ba8ea29a 100644 --- a/packages/dev/s2-docs/scripts/generateOGImages.mjs +++ b/packages/dev/s2-docs/scripts/generateOGImages.mjs @@ -271,9 +271,39 @@ async function getComponentSvg(title) { svgContent = svgContent.replace(/background:\s*#f4f6fc/g, 'background: #f8f8f8'); svgContent = svgContent.replace(/var\(--anatomy-font\)/g, 'adobe-clean'); - // Convert SVG to data URI for use as image source - const svgBase64 = Buffer.from(svgContent).toString('base64'); - return `data:image/svg+xml;base64,${svgBase64}`; + const adobeCleanRegularBase64 = Buffer.from(adobeCleanRegular).toString('base64'); + const adobeCleanBoldBase64 = Buffer.from(adobeCleanBold).toString('base64'); + + const fontFaceStyle = ` + @font-face { + font-family: 'adobe-clean'; + font-style: normal; + font-weight: 400; + src: url(data:font/woff2;base64,${adobeCleanRegularBase64}) format('woff2'); + } + @font-face { + font-family: 'adobe-clean'; + font-style: normal; + font-weight: 700; + src: url(data:font/woff2;base64,${adobeCleanBoldBase64}) format('woff2'); + } + `; + + // Inject style tag into SVG + if (svgContent.includes('')) { + svgContent = svgContent.replace('', ``); + } else if (svgContent.includes(']*)>/, ``); + } + + // Convert SVG to PNG + const pngBuffer = await sharp(Buffer.from(svgContent)) + .png() + .toBuffer(); + + // Convert PNG to data URI to use as image source + const pngBase64 = pngBuffer.toString('base64'); + return `data:image/png;base64,${pngBase64}`; } catch (error) { console.warn(`Could not load SVG for ${title}: ${error.message}`); return null; From 3b414ec0e24e722c0e5a723fab0ab682527b0865 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Wed, 8 Oct 2025 17:07:29 -0500 Subject: [PATCH 14/26] Revert "try inlining font-face and pre-rendering illustration as png" This reverts commit 5b6c0029d30ee4323d31053c1c31dace7be6a440. --- .../dev/s2-docs/scripts/generateOGImages.mjs | 36 ++----------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/packages/dev/s2-docs/scripts/generateOGImages.mjs b/packages/dev/s2-docs/scripts/generateOGImages.mjs index 7c7ba8ea29a..69cacf57edd 100644 --- a/packages/dev/s2-docs/scripts/generateOGImages.mjs +++ b/packages/dev/s2-docs/scripts/generateOGImages.mjs @@ -271,39 +271,9 @@ async function getComponentSvg(title) { svgContent = svgContent.replace(/background:\s*#f4f6fc/g, 'background: #f8f8f8'); svgContent = svgContent.replace(/var\(--anatomy-font\)/g, 'adobe-clean'); - const adobeCleanRegularBase64 = Buffer.from(adobeCleanRegular).toString('base64'); - const adobeCleanBoldBase64 = Buffer.from(adobeCleanBold).toString('base64'); - - const fontFaceStyle = ` - @font-face { - font-family: 'adobe-clean'; - font-style: normal; - font-weight: 400; - src: url(data:font/woff2;base64,${adobeCleanRegularBase64}) format('woff2'); - } - @font-face { - font-family: 'adobe-clean'; - font-style: normal; - font-weight: 700; - src: url(data:font/woff2;base64,${adobeCleanBoldBase64}) format('woff2'); - } - `; - - // Inject style tag into SVG - if (svgContent.includes('')) { - svgContent = svgContent.replace('', ``); - } else if (svgContent.includes(']*)>/, ``); - } - - // Convert SVG to PNG - const pngBuffer = await sharp(Buffer.from(svgContent)) - .png() - .toBuffer(); - - // Convert PNG to data URI to use as image source - const pngBase64 = pngBuffer.toString('base64'); - return `data:image/png;base64,${pngBase64}`; + // Convert SVG to data URI for use as image source + const svgBase64 = Buffer.from(svgContent).toString('base64'); + return `data:image/svg+xml;base64,${svgBase64}`; } catch (error) { console.warn(`Could not load SVG for ${title}: ${error.message}`); return null; From 8c5a3deee3f7075cc73b91a83e2f984fcfa22363 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Wed, 8 Oct 2025 17:19:37 -0500 Subject: [PATCH 15/26] try using fontconfig in CI --- .circleci/config.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index b7665dbc8f5..eea0e26ac53 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -484,6 +484,16 @@ jobs: - restore_cache: key: react-spectrum-{{ .Environment.CACHE_VERSION }}-{{ .Environment.CIRCLE_SHA1 }} + - run: + name: install fontconfig and fonts + command: | + apt-get update + apt-get install -y fontconfig wget + mkdir -p /usr/local/share/fonts + wget -O /usr/local/share/fonts/AdobeClean-Regular.ttf "https://use.typekit.net/af/cb695f/000000000000000000017701/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3" + wget -O /usr/local/share/fonts/AdobeClean-Bold.ttf "https://use.typekit.net/af/eaf09c/000000000000000000017703/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3" + fc-cache -f -v + - run: name: build s2 docs command: make s2-docs From 7ccf411fc45ed5f2e1b5ea707e14bfa2b760c80b Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Wed, 8 Oct 2025 17:24:07 -0500 Subject: [PATCH 16/26] update command --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index eea0e26ac53..37f4281e2bb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -487,8 +487,8 @@ jobs: - run: name: install fontconfig and fonts command: | - apt-get update - apt-get install -y fontconfig wget + sudo apt-get update + sudo apt-get install -y fontconfig wget mkdir -p /usr/local/share/fonts wget -O /usr/local/share/fonts/AdobeClean-Regular.ttf "https://use.typekit.net/af/cb695f/000000000000000000017701/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3" wget -O /usr/local/share/fonts/AdobeClean-Bold.ttf "https://use.typekit.net/af/eaf09c/000000000000000000017703/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3" From 4373fab35e231545ed8eeb723ec3bf495b9fac11 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Wed, 8 Oct 2025 17:28:37 -0500 Subject: [PATCH 17/26] more CI updates --- .circleci/config.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 37f4281e2bb..f5a6fcea23a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -489,10 +489,10 @@ jobs: command: | sudo apt-get update sudo apt-get install -y fontconfig wget - mkdir -p /usr/local/share/fonts - wget -O /usr/local/share/fonts/AdobeClean-Regular.ttf "https://use.typekit.net/af/cb695f/000000000000000000017701/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3" - wget -O /usr/local/share/fonts/AdobeClean-Bold.ttf "https://use.typekit.net/af/eaf09c/000000000000000000017703/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3" - fc-cache -f -v + sudo mkdir -p /usr/local/share/fonts + sudo wget -O /usr/local/share/fonts/AdobeClean-Regular.ttf "https://use.typekit.net/af/cb695f/000000000000000000017701/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3" + sudo wget -O /usr/local/share/fonts/AdobeClean-Bold.ttf "https://use.typekit.net/af/eaf09c/000000000000000000017703/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3" + sudo fc-cache -f -v - run: name: build s2 docs From 70a34092b073f4e50d7d68b777797d850a94fcc7 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 9 Oct 2025 10:05:50 -0500 Subject: [PATCH 18/26] strip out version badge from title --- packages/dev/s2-docs/scripts/generateOGImages.mjs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/dev/s2-docs/scripts/generateOGImages.mjs b/packages/dev/s2-docs/scripts/generateOGImages.mjs index 69cacf57edd..595512ccb27 100644 --- a/packages/dev/s2-docs/scripts/generateOGImages.mjs +++ b/packages/dev/s2-docs/scripts/generateOGImages.mjs @@ -53,7 +53,11 @@ async function getTitle(filePath) { } let match = parsed.content.match(/^#\s+(.+)$/m); if (match) { - return match[1].trim(); + let title = match[1].trim(); + // Strip out any React components (like ) + title = title.replace(/<[A-Z]\w*[^>]*\/>/g, '').trim(); + title = title.replace(/<[A-Z]\w*[^>]*>.*?<\/[A-Z]\w*>/g, '').trim(); + return title; } return path.basename(filePath, path.extname(filePath)); } From f85caafcc4df2cf5323d65901dc9fb435d5f63b8 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 9 Oct 2025 10:12:09 -0500 Subject: [PATCH 19/26] remove illustration SVGs for now --- .circleci/config.yml | 10 -- .../dev/s2-docs/scripts/generateOGImages.mjs | 170 +----------------- 2 files changed, 2 insertions(+), 178 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f5a6fcea23a..b7665dbc8f5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -484,16 +484,6 @@ jobs: - restore_cache: key: react-spectrum-{{ .Environment.CACHE_VERSION }}-{{ .Environment.CIRCLE_SHA1 }} - - run: - name: install fontconfig and fonts - command: | - sudo apt-get update - sudo apt-get install -y fontconfig wget - sudo mkdir -p /usr/local/share/fonts - sudo wget -O /usr/local/share/fonts/AdobeClean-Regular.ttf "https://use.typekit.net/af/cb695f/000000000000000000017701/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3" - sudo wget -O /usr/local/share/fonts/AdobeClean-Bold.ttf "https://use.typekit.net/af/eaf09c/000000000000000000017703/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3" - sudo fc-cache -f -v - - run: name: build s2 docs command: make s2-docs diff --git a/packages/dev/s2-docs/scripts/generateOGImages.mjs b/packages/dev/s2-docs/scripts/generateOGImages.mjs index 595512ccb27..b79911598a5 100644 --- a/packages/dev/s2-docs/scripts/generateOGImages.mjs +++ b/packages/dev/s2-docs/scripts/generateOGImages.mjs @@ -10,26 +10,6 @@ const __dirname = path.dirname(__filename); const pagesDir = path.resolve(__dirname, '../pages'); const outputDir = path.resolve(__dirname, '../dist/og'); -const illustrationsDir = path.resolve(__dirname, '../../docs/pages/assets/component-illustrations'); - -const cssVariables = { - '--anatomy-gray-50': '#FFFFFF', - '--anatomy-gray-75': '#FDFDFE', - '--anatomy-gray-100': '#f4f6fc', - '--anatomy-gray-200': '#E5EBF7', - '--anatomy-gray-300': '#DAE2F4', - '--anatomy-gray-400': '#beccea', - '--anatomy-gray-500': '#a2b6e1', - '--anatomy-gray-600': '#718dcf', - '--anatomy-gray-700': '#4a6fc3', - '--anatomy-gray-800': '#496EC2', - '--anatomy-gray-900': '#486EC2', - '--spectrum-global-color-gray-300': '#d3d3d3', - '--spectrum-global-color-gray-400': '#bcbcbc', - '--spectrum-global-color-gray-700': '#464646', - '--spectrum-alias-border-color-focus': '#0f62fe', - '--anatomy-font': 'adobe-clean' -}; async function getMdxFiles(dir) { let entries = await fs.readdir(dir, {withFileTypes: true}); @@ -93,19 +73,6 @@ const [adobeCleanRegular, adobeCleanBold] = await Promise.all([ loadFont('https://use.typekit.net/af/eaf09c/000000000000000000017703/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3') ]); -// Mappings for components that don't match their SVG file names -const componentSvgExceptions = { - 'Accordion': 'DisclosureGroup.svg', - 'ActionButtonGroup': 'ButtonGroup.svg', // TODO: get better illustration - 'ActionMenu': 'Menu.svg', - 'Drag and Drop': 'DragAndDrop.svg', - 'GridList': 'ListView.svg', - 'LinkButton': 'Button.svg', // TODO: get better illustration - 'Select': 'Picker.svg', - 'TableView': 'Table.svg', - 'TreeView': 'Tree.svg' -}; - function getLibraryLogo(subtitle) { if (subtitle === 'React Aria') { return { @@ -252,38 +219,6 @@ function getLibraryLogo(subtitle) { } } -async function getComponentSvg(title) { - // First try exception mappings - let svgFileName = componentSvgExceptions[title]; - - // If no exception mapping, try automatic name matching - if (!svgFileName) { - svgFileName = `${title}.svg`; - } - - try { - const svgPath = path.join(illustrationsDir, svgFileName); - let svgContent = await fs.readFile(svgPath, 'utf8'); - - // Replace CSS variables with actual colors - for (const [variable, color] of Object.entries(cssVariables)) { - svgContent = svgContent.replace(new RegExp(`var\\(${variable.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\)`, 'g'), color); - } - - // Replace SVG background to match container background - svgContent = svgContent.replace(/background:\s*var\(--anatomy-gray-100\)/g, 'background: #f8f8f8'); - svgContent = svgContent.replace(/background:\s*#f4f6fc/g, 'background: #f8f8f8'); - svgContent = svgContent.replace(/var\(--anatomy-font\)/g, 'adobe-clean'); - - // Convert SVG to data URI for use as image source - const svgBase64 = Buffer.from(svgContent).toString('base64'); - return `data:image/svg+xml;base64,${svgBase64}`; - } catch (error) { - console.warn(`Could not load SVG for ${title}: ${error.message}`); - return null; - } -} - await fs.mkdir(outputDir, {recursive: true}); const files = await getMdxFiles(pagesDir); console.log(`Generating OG images for ${files.length} pages…`); @@ -297,9 +232,6 @@ for (let file of files) { let subtitle = getSubtitle(slug); let isIndexPage = slug === 'index' || slug.endsWith('/index'); - // Get component SVG if available - const componentSvg = await getComponentSvg(title); - // Determine layout type let layout; if (isIndexPage) { @@ -345,8 +277,8 @@ for (let file of files) { } } }; - } else if (!componentSvg) { - // Page without illustration: Centered logo + page title + library name + } else { + // Regular page layout: Centered logo + page title + library name layout = { type: 'div', props: { @@ -411,104 +343,6 @@ for (let file of files) { } } }; - } else { - // Component page layout: Component illustration + bottom section - layout = { - type: 'div', - props: { - style: { - display: 'flex', - flexDirection: 'column', - width: '100%', - height: '100%', - backgroundColor: '#ffffff', - fontFamily: 'adobe-clean', - color: '#000000' - }, - children: [ - // Top section: Component illustration - { - type: 'div', - props: { - style: { - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - width: '100%', - height: '394px', // 10/16 of 630px total height - backgroundColor: '#f8f8f8', - padding: '40px', - borderBottom: '1px solid #e5e5e5' - }, - children: { - type: 'img', - props: { - src: componentSvg, - style: { - width: '340px', - height: '320px', - objectFit: 'contain' - } - } - } - } - }, - // Bottom section: Library logo + text - { - type: 'div', - props: { - style: { - display: 'flex', - alignItems: 'center', - width: '100%', - height: '236px', // 6/16 of 630px total height - padding: '0 60px', - gap: 40 - }, - children: [ - // Library logo (Adobe for React Spectrum, React Aria, or Internationalized) - getLibraryLogo(subtitle), - // Text content - { - type: 'div', - props: { - style: { - display: 'flex', - flexDirection: 'column', - gap: 8 - }, - children: [ - { - type: 'div', - props: { - style: { - fontSize: 84, - fontWeight: 700, - lineHeight: 1.1 - }, - children: title - } - }, - { - type: 'div', - props: { - style: { - fontSize: 56, - fontWeight: 400, - color: '#464646' - }, - children: subtitle - } - } - ] - } - } - ] - } - } - ] - } - }; } let svg = await satori( From 911889fa7f1eaf68f2992069c83e4629ad8265ea Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 9 Oct 2025 10:18:48 -0500 Subject: [PATCH 20/26] improve gap --- packages/dev/s2-docs/scripts/generateOGImages.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dev/s2-docs/scripts/generateOGImages.mjs b/packages/dev/s2-docs/scripts/generateOGImages.mjs index b79911598a5..0cd9c986652 100644 --- a/packages/dev/s2-docs/scripts/generateOGImages.mjs +++ b/packages/dev/s2-docs/scripts/generateOGImages.mjs @@ -310,7 +310,7 @@ for (let file of files) { style: { display: 'flex', flexDirection: 'column', - gap: 8 + gap: 0 }, children: [ { From c089638e42eee28706a37f13b2a60e43f7c07165 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 9 Oct 2025 11:19:06 -0500 Subject: [PATCH 21/26] layout improvements --- packages/dev/s2-docs/scripts/generateOGImages.mjs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/dev/s2-docs/scripts/generateOGImages.mjs b/packages/dev/s2-docs/scripts/generateOGImages.mjs index 0cd9c986652..54671467664 100644 --- a/packages/dev/s2-docs/scripts/generateOGImages.mjs +++ b/packages/dev/s2-docs/scripts/generateOGImages.mjs @@ -79,7 +79,7 @@ function getLibraryLogo(subtitle) { type: 'svg', props: { width: 156, - height: 138, + height: 150, viewBox: '15.79 17.64 131.21 126.14', xmlns: 'http://www.w3.org/2000/svg', children: [ @@ -131,7 +131,7 @@ function getLibraryLogo(subtitle) { type: 'svg', props: { width: 156, - height: 138, + height: 148, viewBox: '15 17 135 128', xmlns: 'http://www.w3.org/2000/svg', children: [ @@ -255,7 +255,7 @@ for (let file of files) { style: { display: 'flex', alignItems: 'center', - gap: 40 + gap: 44 }, children: [ // Library logo @@ -298,7 +298,7 @@ for (let file of files) { style: { display: 'flex', alignItems: 'center', - gap: 40 + gap: 44 }, children: [ // Library logo From 90719856fd07058b94ba066bff9d8303aa9b92e8 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 9 Oct 2025 11:20:55 -0500 Subject: [PATCH 22/26] skip error page --- packages/dev/s2-docs/scripts/generateOGImages.mjs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/dev/s2-docs/scripts/generateOGImages.mjs b/packages/dev/s2-docs/scripts/generateOGImages.mjs index 54671467664..aac8b044589 100644 --- a/packages/dev/s2-docs/scripts/generateOGImages.mjs +++ b/packages/dev/s2-docs/scripts/generateOGImages.mjs @@ -229,6 +229,12 @@ for (let file of files) { .relative(pagesDir, file) .replace(/\\/g, '/') .replace(/\.mdx?$/, ''); + + // Skip the error page + if (slug === 'error') { + continue; + } + let subtitle = getSubtitle(slug); let isIndexPage = slug === 'index' || slug.endsWith('/index'); From 2f2e1cb3d0dd1ab0f66a5cb90053d2b081841665 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 9 Oct 2025 11:38:21 -0500 Subject: [PATCH 23/26] add Internationalized to library in name --- packages/dev/s2-docs/src/Layout.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/dev/s2-docs/src/Layout.tsx b/packages/dev/s2-docs/src/Layout.tsx index 8107abf74d1..efd771dc7ae 100644 --- a/packages/dev/s2-docs/src/Layout.tsx +++ b/packages/dev/s2-docs/src/Layout.tsx @@ -50,6 +50,9 @@ const getLibraryName = (currentPage: Page): string => { if (currentPage.name.startsWith('react-aria/')) { return 'React Aria'; } + if (currentPage.name.startsWith('internationalized/')) { + return 'Internationalized'; + } return 'React Spectrum'; }; From 19c75bba34cc067470f0ef99c2dec931a367c652 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 9 Oct 2025 12:03:15 -0500 Subject: [PATCH 24/26] fix page title in Internationalized --- packages/dev/s2-docs/src/Layout.tsx | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/packages/dev/s2-docs/src/Layout.tsx b/packages/dev/s2-docs/src/Layout.tsx index efd771dc7ae..87bb0e52dce 100644 --- a/packages/dev/s2-docs/src/Layout.tsx +++ b/packages/dev/s2-docs/src/Layout.tsx @@ -7,6 +7,7 @@ import {ClassAPI} from './ClassAPI'; import {Code} from './Code'; import {CodeBlock} from './CodeBlock'; import {ExampleSwitcher} from './ExampleSwitcher'; +import {getLibraryFromPage, getLibraryLabel} from './library'; import {H2, H3, H4} from './Headings'; import Header from './Header'; import {Link} from './Link'; @@ -46,18 +47,8 @@ function anchorId(children) { return children.replace(/\s/g, '-').replace(/[^a-zA-Z0-9-_]/g, '').toLowerCase(); } -const getLibraryName = (currentPage: Page): string => { - if (currentPage.name.startsWith('react-aria/')) { - return 'React Aria'; - } - if (currentPage.name.startsWith('internationalized/')) { - return 'Internationalized'; - } - return 'React Spectrum'; -}; - const getTitle = (currentPage: Page): string => { - let library = getLibraryName(currentPage); + let library = getLibraryLabel(getLibraryFromPage(currentPage)); const pageTitle = currentPage.exports?.title ?? currentPage.tableOfContents?.[0]?.title ?? currentPage.name; return library ? `${pageTitle} - ${library}` : pageTitle; }; @@ -76,7 +67,7 @@ const getOgImageUrl = (currentPage: Page): string => { }; const getDescription = (currentPage: Page): string => { - let library = getLibraryName(currentPage); + let library = getLibraryLabel(getLibraryFromPage(currentPage)); const pageTitle = currentPage.exports?.title ?? currentPage.tableOfContents?.[0]?.title ?? currentPage.name; const explicitDescription = (currentPage as any).description || currentPage.exports?.description; if (explicitDescription) { From 5a74535b7167ca73b83f7fc3c89f7341c34058d8 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 9 Oct 2025 13:07:43 -0500 Subject: [PATCH 25/26] improve titles and descriptions --- packages/dev/s2-docs/pages/error.mdx | 1 + packages/dev/s2-docs/pages/internationalized/date/index.mdx | 1 + .../dev/s2-docs/pages/internationalized/number/index.mdx | 1 + packages/dev/s2-docs/pages/react-aria/collections.mdx | 1 + packages/dev/s2-docs/pages/react-aria/dnd.mdx | 1 + packages/dev/s2-docs/pages/react-aria/selection.mdx | 1 + packages/dev/s2-docs/pages/react-aria/styling.mdx | 1 + packages/dev/s2-docs/pages/s2/collections.mdx | 2 +- packages/dev/s2-docs/pages/s2/dnd.mdx | 2 +- packages/dev/s2-docs/pages/s2/getting-started.mdx | 1 + packages/dev/s2-docs/pages/s2/migrating.mdx | 1 + packages/dev/s2-docs/pages/s2/release-notes.mdx | 1 + packages/dev/s2-docs/pages/s2/selection.mdx | 1 + packages/dev/s2-docs/pages/s2/styling.mdx | 1 + packages/dev/s2-docs/src/Layout.tsx | 5 ++++- 15 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/dev/s2-docs/pages/error.mdx b/packages/dev/s2-docs/pages/error.mdx index 82201a6dc99..fbffab8eeab 100644 --- a/packages/dev/s2-docs/pages/error.mdx +++ b/packages/dev/s2-docs/pages/error.mdx @@ -5,5 +5,6 @@ export default Layout; import docs from 'docs:@react-spectrum/s2'; export const hideFromSearch = true; +export const description = 'Page not found'; diff --git a/packages/dev/s2-docs/pages/internationalized/date/index.mdx b/packages/dev/s2-docs/pages/internationalized/date/index.mdx index 338e75aae47..0635dc4d3b5 100644 --- a/packages/dev/s2-docs/pages/internationalized/date/index.mdx +++ b/packages/dev/s2-docs/pages/internationalized/date/index.mdx @@ -13,6 +13,7 @@ export default Layout; import {InstallCommand} from '../../../src/InstallCommand'; export const section = 'Date and Time'; +export const description = 'Introduction to @internationalized/date'; # Introduction diff --git a/packages/dev/s2-docs/pages/internationalized/number/index.mdx b/packages/dev/s2-docs/pages/internationalized/number/index.mdx index 67db911d372..85f13493d22 100644 --- a/packages/dev/s2-docs/pages/internationalized/number/index.mdx +++ b/packages/dev/s2-docs/pages/internationalized/number/index.mdx @@ -13,6 +13,7 @@ export default Layout; import {InstallCommand} from '../../../src/InstallCommand'; export const section = 'Numbers'; +export const description = 'Introduction to @internationalized/number'; # Introduction diff --git a/packages/dev/s2-docs/pages/react-aria/collections.mdx b/packages/dev/s2-docs/pages/react-aria/collections.mdx index 820768f3fc7..733241582e9 100644 --- a/packages/dev/s2-docs/pages/react-aria/collections.mdx +++ b/packages/dev/s2-docs/pages/react-aria/collections.mdx @@ -5,6 +5,7 @@ import docs from 'docs:react-aria-components'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const section = 'Guides'; +export const description = 'Implementing collections in React Aria'; # Collections diff --git a/packages/dev/s2-docs/pages/react-aria/dnd.mdx b/packages/dev/s2-docs/pages/react-aria/dnd.mdx index ef95686aefd..7c4eea7874c 100644 --- a/packages/dev/s2-docs/pages/react-aria/dnd.mdx +++ b/packages/dev/s2-docs/pages/react-aria/dnd.mdx @@ -13,6 +13,7 @@ import {PokemonListBox} from './PokemonListBox'; import {PokemonGridList} from './PokemonGridList'; export const section = 'Guides'; +export const description = 'Implementing drag and drop in React Aria'; # Drag and Drop diff --git a/packages/dev/s2-docs/pages/react-aria/selection.mdx b/packages/dev/s2-docs/pages/react-aria/selection.mdx index 44ee6a777f8..c7800d9ed3e 100644 --- a/packages/dev/s2-docs/pages/react-aria/selection.mdx +++ b/packages/dev/s2-docs/pages/react-aria/selection.mdx @@ -4,6 +4,7 @@ export default Layout; import docs from 'docs:react-aria-components'; export const section = 'Guides'; +export const description = 'Implementing selection in React Aria'; # Selection diff --git a/packages/dev/s2-docs/pages/react-aria/styling.mdx b/packages/dev/s2-docs/pages/react-aria/styling.mdx index bbe1422e335..1c11d96bf68 100644 --- a/packages/dev/s2-docs/pages/react-aria/styling.mdx +++ b/packages/dev/s2-docs/pages/react-aria/styling.mdx @@ -5,6 +5,7 @@ export default Layout; import {Disclosure, DisclosureTitle, DisclosurePanel} from '@react-spectrum/s2'; export const section = 'Guides'; +export const description = 'Styling in React Aria'; # Styling diff --git a/packages/dev/s2-docs/pages/s2/collections.mdx b/packages/dev/s2-docs/pages/s2/collections.mdx index 8dacb61c5c7..8c41d643502 100644 --- a/packages/dev/s2-docs/pages/s2/collections.mdx +++ b/packages/dev/s2-docs/pages/s2/collections.mdx @@ -3,8 +3,8 @@ import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; export default Layout; export const section = 'Guides'; - export const tags = ['lists']; +export const description = 'Implementing collections in React Spectrum'; # Collections diff --git a/packages/dev/s2-docs/pages/s2/dnd.mdx b/packages/dev/s2-docs/pages/s2/dnd.mdx index 63b2ecfd2df..f96c8231499 100644 --- a/packages/dev/s2-docs/pages/s2/dnd.mdx +++ b/packages/dev/s2-docs/pages/s2/dnd.mdx @@ -3,8 +3,8 @@ import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; export default Layout; export const section = 'Guides'; - export const tags = ['drag', 'drop']; +export const description = 'Implementing drag and drop in React Spectrum'; # Drag and Drop diff --git a/packages/dev/s2-docs/pages/s2/getting-started.mdx b/packages/dev/s2-docs/pages/s2/getting-started.mdx index 87741997513..13ea383c07f 100644 --- a/packages/dev/s2-docs/pages/s2/getting-started.mdx +++ b/packages/dev/s2-docs/pages/s2/getting-started.mdx @@ -6,6 +6,7 @@ import {SegmentedControl, SegmentedControlItem} from '@react-spectrum/s2'; export const section = 'Getting started'; export const tags = ['introduction', 'installation']; +export const description = 'Getting started with React Spectrum'; # Getting started diff --git a/packages/dev/s2-docs/pages/s2/migrating.mdx b/packages/dev/s2-docs/pages/s2/migrating.mdx index 1b42d053070..be6cd9cb4c9 100644 --- a/packages/dev/s2-docs/pages/s2/migrating.mdx +++ b/packages/dev/s2-docs/pages/s2/migrating.mdx @@ -6,6 +6,7 @@ export default Layout; export const section = 'Guides'; export const tags = ['codemod', 'upgrade', 'update']; +export const description = 'Migrating to Spectrum 2 in React Spectrum'; # Migrating to Spectrum 2 diff --git a/packages/dev/s2-docs/pages/s2/release-notes.mdx b/packages/dev/s2-docs/pages/s2/release-notes.mdx index 1f6cc33097d..70f73c8765e 100644 --- a/packages/dev/s2-docs/pages/s2/release-notes.mdx +++ b/packages/dev/s2-docs/pages/s2/release-notes.mdx @@ -3,6 +3,7 @@ export default Layout; export const section = 'Releases'; export const tags = ['changelog', 'versions', 'updates']; +export const description = 'Release notes for React Spectrum'; # Release Notes diff --git a/packages/dev/s2-docs/pages/s2/selection.mdx b/packages/dev/s2-docs/pages/s2/selection.mdx index 2bc589dd40e..7a54a0897d4 100644 --- a/packages/dev/s2-docs/pages/s2/selection.mdx +++ b/packages/dev/s2-docs/pages/s2/selection.mdx @@ -4,6 +4,7 @@ export default Layout; export const section = 'Guides'; export const tags = ['collections']; +export const description = 'Implementing selection in React Spectrum'; # Selection diff --git a/packages/dev/s2-docs/pages/s2/styling.mdx b/packages/dev/s2-docs/pages/s2/styling.mdx index 59e257b206d..3735835c868 100644 --- a/packages/dev/s2-docs/pages/s2/styling.mdx +++ b/packages/dev/s2-docs/pages/s2/styling.mdx @@ -7,6 +7,7 @@ export default Layout; export const section = 'Guides'; export const tags = ['style', 'macro', 'spectrum', 'custom']; +export const description = 'Styling in React Spectrum'; # Styling diff --git a/packages/dev/s2-docs/src/Layout.tsx b/packages/dev/s2-docs/src/Layout.tsx index 87bb0e52dce..750a84775a8 100644 --- a/packages/dev/s2-docs/src/Layout.tsx +++ b/packages/dev/s2-docs/src/Layout.tsx @@ -50,7 +50,7 @@ function anchorId(children) { const getTitle = (currentPage: Page): string => { let library = getLibraryLabel(getLibraryFromPage(currentPage)); const pageTitle = currentPage.exports?.title ?? currentPage.tableOfContents?.[0]?.title ?? currentPage.name; - return library ? `${pageTitle} - ${library}` : pageTitle; + return library ? `${pageTitle} | ${library}` : pageTitle; }; const getOgImageUrl = (currentPage: Page): string => { @@ -73,6 +73,9 @@ const getDescription = (currentPage: Page): string => { if (explicitDescription) { return explicitDescription as string; } + if (currentPage.name === 'index.html' || currentPage.name.endsWith('/index.html')) { + return `Documentation for ${library || 'React Spectrum'}`; + } return library ? `Documentation for ${pageTitle} in ${library}.` : `Documentation for ${pageTitle}.`; }; From 646162da1701c663993ad202c72979782f86f044 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 9 Oct 2025 13:31:38 -0500 Subject: [PATCH 26/26] improve titles for index pages and explicit custom titles --- .../s2-docs/pages/internationalized/date/index.mdx | 1 + .../s2-docs/pages/internationalized/number/index.mdx | 1 + packages/dev/s2-docs/src/Layout.tsx | 12 +++++++++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/dev/s2-docs/pages/internationalized/date/index.mdx b/packages/dev/s2-docs/pages/internationalized/date/index.mdx index 0635dc4d3b5..a1b76f4d827 100644 --- a/packages/dev/s2-docs/pages/internationalized/date/index.mdx +++ b/packages/dev/s2-docs/pages/internationalized/date/index.mdx @@ -13,6 +13,7 @@ export default Layout; import {InstallCommand} from '../../../src/InstallCommand'; export const section = 'Date and Time'; +export const pageTitle = 'Internationalized | Date and Time'; export const description = 'Introduction to @internationalized/date'; # Introduction diff --git a/packages/dev/s2-docs/pages/internationalized/number/index.mdx b/packages/dev/s2-docs/pages/internationalized/number/index.mdx index 85f13493d22..4d91a1c507d 100644 --- a/packages/dev/s2-docs/pages/internationalized/number/index.mdx +++ b/packages/dev/s2-docs/pages/internationalized/number/index.mdx @@ -13,6 +13,7 @@ export default Layout; import {InstallCommand} from '../../../src/InstallCommand'; export const section = 'Numbers'; +export const pageTitle = 'Internationalized | Numbers'; export const description = 'Introduction to @internationalized/number'; # Introduction diff --git a/packages/dev/s2-docs/src/Layout.tsx b/packages/dev/s2-docs/src/Layout.tsx index 750a84775a8..66ba59a9936 100644 --- a/packages/dev/s2-docs/src/Layout.tsx +++ b/packages/dev/s2-docs/src/Layout.tsx @@ -48,8 +48,18 @@ function anchorId(children) { } const getTitle = (currentPage: Page): string => { + const explicitTitle = (currentPage as any).pageTitle || currentPage.exports?.pageTitle; + if (explicitTitle && explicitTitle !== currentPage.tableOfContents?.[0]?.title && explicitTitle !== currentPage.name) { + return explicitTitle as string; + } + let library = getLibraryLabel(getLibraryFromPage(currentPage)); - const pageTitle = currentPage.exports?.title ?? currentPage.tableOfContents?.[0]?.title ?? currentPage.name; + const pageTitle = currentPage.tableOfContents?.[0]?.title ?? currentPage.name; + + if (currentPage.name === 'index.html' || currentPage.name.endsWith('/index.html')) { + return library || 'React Spectrum'; + } + return library ? `${pageTitle} | ${library}` : pageTitle; };