Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/docsite/public/theme-assets/Gothic-1.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/docsite/public/theme-assets/Gothic-2.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/docsite/public/theme-assets/Gothic-3.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/docsite/public/theme-assets/Gothic-4.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/docsite/public/theme-assets/Gothic-5.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/docsite/public/theme-assets/Gothic-6.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/docsite/public/theme-assets/Stone-cup.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/docsite/public/theme-assets/Y2K-Couch.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/docsite/public/theme-assets/Y2K-Heart.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/docsite/public/theme-assets/Y2K-Phone.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/docsite/public/theme-assets/Y2K-Star.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 16 additions & 15 deletions apps/docsite/src/components/ThemePackagePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ import commandBlockStyles from './ThemeCommandBlock.module.css';
const THEME_SHOWCASE_SOURCE =
templates.find(t => t.slug === 'theme-showcase')?.source ?? '';

// CDN host for the per-theme picker banners (same host as the showcase
// product photos), so the artwork can be updated without a code change.
const PICKER_CDN = 'https://lookaside.facebook.com/assets/astryx';
// Local per-theme artwork used by the picker banners and showcase product
// photos. Keeping these assets same-origin avoids flaky cross-browser CDN
// fetches on /themes.
const THEME_ASSETS = '/theme-assets';

// Gallery order — themes are listed in the same canonical visual-
// closeness order used elsewhere (most restrained → most expressive).
Expand Down Expand Up @@ -293,51 +294,51 @@ const styles = stylex.create({
// Per-theme bespoke picker artwork — one rule per theme that
// has a custom photo (vs. the multi-radial-gradient default).
// Each rule sets the picker card's background to a dedicated
// theme-<slug>-picker.png banner hosted on the Astryx asset CDN
// (see PICKER_CDN). These are SEPARATE files from the
// theme-<slug>-preview.png images used on the /themes overview +
// theme-<slug>-picker.webp banner served from /theme-assets.
// These are SEPARATE files from the theme-<slug>-preview.png images
// used on the /themes overview +
// detail page hero — these picker assets are sized and
// art-directed for the small 120px-tall picker card.
// background-size:cover so each image always fills the card
// regardless of the source's intrinsic dimensions.
surfaceButter: {
backgroundColor: 'transparent',
backgroundImage: `url(${PICKER_CDN}/theme-butter-picker.png)`,
backgroundImage: `url(${THEME_ASSETS}/theme-butter-picker.webp)`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
},
surfaceGothic: {
backgroundColor: 'transparent',
backgroundImage: `url(${PICKER_CDN}/theme-gothic-picker.png)`,
backgroundImage: `url(${THEME_ASSETS}/theme-gothic-picker.webp)`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
},
surfaceY2k: {
backgroundColor: 'transparent',
backgroundImage: `url(${PICKER_CDN}/theme-y2k-picker.png)`,
backgroundImage: `url(${THEME_ASSETS}/theme-y2k-picker.webp)`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
},
surfaceStone: {
backgroundColor: 'transparent',
backgroundImage: `url(${PICKER_CDN}/theme-stone-picker.png)`,
backgroundImage: `url(${THEME_ASSETS}/theme-stone-picker.webp)`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
},
surfaceNeutral: {
backgroundColor: 'transparent',
backgroundImage: `url(${PICKER_CDN}/theme-neutral-picker.png)`,
backgroundImage: `url(${THEME_ASSETS}/theme-neutral-picker.webp)`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
},
surfaceMatcha: {
backgroundColor: 'transparent',
backgroundImage: `url(${PICKER_CDN}/theme-matcha-picker.png)`,
backgroundImage: `url(${THEME_ASSETS}/theme-matcha-picker.webp)`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
Expand Down Expand Up @@ -497,9 +498,9 @@ const styles = stylex.create({
// can swap the card's `surface` (background image) and the `label`
// color (most use labelAccent so the wordmark reads as a brand
// signature on top of the photo). Adding artwork for a new theme
// is a two-step addition: upload a theme-<slug>-picker.png banner to
// the asset CDN (PICKER_CDN) + add a `surface<Name>` rule into the
// styles block above, then reference both here.
// is a two-step addition: add a theme-<slug>-picker.webp banner under
// /public/theme-assets + add a `surface<Name>` rule into the styles
// block above, then reference both here.
const PICKER_OVERRIDES: Record<
string,
{surface: StyleXStyles; label?: StyleXStyles}
Expand Down
62 changes: 31 additions & 31 deletions apps/docsite/src/components/themeShowcaseContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ export interface ThemeShowcaseContent {
inventory: InventoryRow[];
}

const IMAGE_CDN = 'https://lookaside.facebook.com/assets/astryx';
const THEME_ASSETS = '/theme-assets';

// Matcha café — matcha-drink photos.
const MATCHA_IMAGES: Record<string, string> = {
watch: `${IMAGE_CDN}/matcha-product-1.png`,
headphones: `${IMAGE_CDN}/matcha-product-2.png`,
backpack: `${IMAGE_CDN}/matcha-product-3.png`,
wallet: `${IMAGE_CDN}/matcha-product-5.png`,
tumbler: `${IMAGE_CDN}/matcha-product-4.png`,
throw_: `${IMAGE_CDN}/matcha-product-6.png`,
watch: `${THEME_ASSETS}/matcha-product-1.webp`,
headphones: `${THEME_ASSETS}/matcha-product-2.webp`,
backpack: `${THEME_ASSETS}/matcha-product-3.webp`,
wallet: `${THEME_ASSETS}/matcha-product-5.webp`,
tumbler: `${THEME_ASSETS}/matcha-product-4.webp`,
throw_: `${THEME_ASSETS}/matcha-product-6.webp`,
};

const MATCHA_CONTENT: ThemeShowcaseContent = {
Expand Down Expand Up @@ -131,12 +131,12 @@ const MATCHA_CONTENT: ThemeShowcaseContent = {
// Butter bakery — butter/breakfast photos. Only five Butter assets exist, so
// the croissant is reused for the sixth slot.
const BUTTER_IMAGES: Record<string, string> = {
watch: `${IMAGE_CDN}/Butter-Croissant.png`,
headphones: `${IMAGE_CDN}/Butter-Pancake.png`,
backpack: `${IMAGE_CDN}/Butter-Waffle.png`,
wallet: `${IMAGE_CDN}/Butter-Toast.png`,
tumbler: `${IMAGE_CDN}/Butter-Stick.png`,
throw_: `${IMAGE_CDN}/Butter-Croissant.png`,
watch: `${THEME_ASSETS}/Butter-Croissant.webp`,
headphones: `${THEME_ASSETS}/Butter-Pancake.webp`,
backpack: `${THEME_ASSETS}/Butter-Waffle.webp`,
wallet: `${THEME_ASSETS}/Butter-Toast.webp`,
tumbler: `${THEME_ASSETS}/Butter-Stick.webp`,
throw_: `${THEME_ASSETS}/Butter-Croissant.webp`,
};

const BUTTER_CONTENT: ThemeShowcaseContent = {
Expand Down Expand Up @@ -233,12 +233,12 @@ const BUTTER_CONTENT: ThemeShowcaseContent = {

// Stone homeware — stoneware photos on stone plinths.
const STONE_IMAGES: Record<string, string> = {
watch: `${IMAGE_CDN}/Stone-bowl.png`,
headphones: `${IMAGE_CDN}/Stone-cup.png`,
backpack: `${IMAGE_CDN}/Stone-plate.png`,
wallet: `${IMAGE_CDN}/Stone-servingbowl.png`,
tumbler: `${IMAGE_CDN}/Stone-teapot.png`,
throw_: `${IMAGE_CDN}/Stone-vase.png`,
watch: `${THEME_ASSETS}/Stone-bowl.webp`,
headphones: `${THEME_ASSETS}/Stone-cup.webp`,
backpack: `${THEME_ASSETS}/Stone-plate.webp`,
wallet: `${THEME_ASSETS}/Stone-servingbowl.webp`,
tumbler: `${THEME_ASSETS}/Stone-teapot.webp`,
throw_: `${THEME_ASSETS}/Stone-vase.webp`,
};

const STONE_CONTENT: ThemeShowcaseContent = {
Expand Down Expand Up @@ -335,12 +335,12 @@ const STONE_CONTENT: ThemeShowcaseContent = {

// Y2K trinkets — iridescent/holo objects.
const Y2K_IMAGES: Record<string, string> = {
watch: `${IMAGE_CDN}/Y2K-Phone.png`,
headphones: `${IMAGE_CDN}/Y2K-Star.png`,
backpack: `${IMAGE_CDN}/Y2K-Butterfly.png`,
wallet: `${IMAGE_CDN}/Y2K-Heart.png`,
tumbler: `${IMAGE_CDN}/Y2K-Flower.png`,
throw_: `${IMAGE_CDN}/Y2K-Couch.png`,
watch: `${THEME_ASSETS}/Y2K-Phone.webp`,
headphones: `${THEME_ASSETS}/Y2K-Star.webp`,
backpack: `${THEME_ASSETS}/Y2K-Butterfly.webp`,
wallet: `${THEME_ASSETS}/Y2K-Heart.webp`,
tumbler: `${THEME_ASSETS}/Y2K-Flower.webp`,
throw_: `${THEME_ASSETS}/Y2K-Couch.webp`,
};

const Y2K_CONTENT: ThemeShowcaseContent = {
Expand Down Expand Up @@ -437,12 +437,12 @@ const Y2K_CONTENT: ThemeShowcaseContent = {

// Gothic botanicals — moody single-stem floral photos.
const GOTHIC_IMAGES: Record<string, string> = {
watch: `${IMAGE_CDN}/Gothic-1.png`,
headphones: `${IMAGE_CDN}/Gothic-2.png`,
backpack: `${IMAGE_CDN}/Gothic-3.png`,
wallet: `${IMAGE_CDN}/Gothic-4.png`,
tumbler: `${IMAGE_CDN}/Gothic-5.png`,
throw_: `${IMAGE_CDN}/Gothic-6.png`,
watch: `${THEME_ASSETS}/Gothic-1.webp`,
headphones: `${THEME_ASSETS}/Gothic-2.webp`,
backpack: `${THEME_ASSETS}/Gothic-3.webp`,
wallet: `${THEME_ASSETS}/Gothic-4.webp`,
tumbler: `${THEME_ASSETS}/Gothic-5.webp`,
throw_: `${THEME_ASSETS}/Gothic-6.webp`,
};

const GOTHIC_CONTENT: ThemeShowcaseContent = {
Expand Down
28 changes: 23 additions & 5 deletions scripts/check-demo-media.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.

/**
* Source check: Thumbnail block examples must use CORS-safe demo media.
* Source check: image-backed demos that need reliable browser rendering must
* use CORS-safe demo media.
*
* Thumbnail's remove-button overlay uses useImageMode, which FETCHES the image
* (`fetch(src, {mode: 'cors'})` -> createImageBitmap -> OffscreenCanvas
Expand All @@ -19,10 +20,15 @@ import path from 'node:path';
import {fileURLToPath} from 'node:url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const THUMBNAIL_BLOCK_DIR = path.resolve(
__dirname,
'../packages/cli/templates/blocks/components/Thumbnail',
const ROOT = path.resolve(__dirname, '..');
const THUMBNAIL_BLOCK_DIR = path.join(
ROOT,
'packages/cli/templates/blocks/components/Thumbnail',
);
const THEMES_PAGE_FILES = [
'apps/docsite/src/components/ThemePackagePage.tsx',
'apps/docsite/src/components/themeShowcaseContent.ts',
].map(file => path.join(ROOT, file));

// Cross-origin demo CDN that cannot be CORS-sampled by useImageMode.
const CROSS_ORIGIN_MEDIA = 'lookaside.facebook.com';
Expand Down Expand Up @@ -51,6 +57,18 @@ for (const file of files) {
}
}

for (const file of THEMES_PAGE_FILES) {
const source = fs.readFileSync(file, 'utf-8');
checked++;

if (source.includes(CROSS_ORIGIN_MEDIA)) {
errors.push({
file: path.relative(ROOT, file),
issue: `references cross-origin ${CROSS_ORIGIN_MEDIA} media — /themes artwork must be same-origin so Chrome and Firefox do not depend on flaky CDN fetches`,
});
}
}

if (errors.length > 0) {
console.error('❌ Thumbnail demo-media errors:\n');
for (const {file, issue} of errors) {
Expand All @@ -63,5 +81,5 @@ if (errors.length > 0) {
}

console.log(
`✅ ${checked} image-backed Thumbnail example(s) checked — all use CORS-safe demo media.`,
`✅ ${checked} CORS-sensitive demo media source(s) checked — all use same-origin or self-contained media.`,
);