Initial commit
This commit is contained in:
1 parent
1ba45f88aa
commit
9e6804c1ab
77 files changed
+3888
No files matched your search
@@ -0,0 +1,76 @@
|
||||
---
|
||||
import { getImage } from 'astro:assets';
|
||||
import type { ImageMetadata } from 'astro';
|
||||
import { screens, type ScreenName } from '../lib/screens';
|
||||
|
||||
interface Props {
|
||||
name: ScreenName;
|
||||
/** Above the fold: load immediately with high priority. */
|
||||
priority?: boolean;
|
||||
sizes?: string;
|
||||
/** Override the default alt text. */
|
||||
alt?: string;
|
||||
class?: string;
|
||||
}
|
||||
const { name, priority = false, sizes = '(min-width: 1160px) 1120px, 100vw', alt, class: className } = Astro.props;
|
||||
const spec = screens[name];
|
||||
|
||||
const files = import.meta.glob<{ default: ImageMetadata }>('/src/assets/screens/*.{png,jpg,jpeg,webp,svg}', { eager: true });
|
||||
const raster = ['png', 'webp', 'jpg', 'jpeg'];
|
||||
|
||||
/** Real captures (png/webp/jpg) beat placeholder SVGs; `<name>-<theme>` beats a bare `<name>`. */
|
||||
function pick(theme: 'dark' | 'light'): ImageMetadata {
|
||||
const tries = [
|
||||
...raster.map((e) => `${name}-${theme}.${e}`),
|
||||
...raster.map((e) => `${name}.${e}`),
|
||||
`${name}-${theme}.svg`,
|
||||
`${name}.svg`,
|
||||
];
|
||||
for (const t of tries) {
|
||||
const hit = files[`/src/assets/screens/${t}`];
|
||||
if (hit) return hit.default;
|
||||
}
|
||||
throw new Error(`No screenshot found for "${name}" (${theme}) in src/assets/screens/`);
|
||||
}
|
||||
/** Optimised webp srcset for real captures; SVG placeholders pass through untouched. */
|
||||
async function sources(img: ImageMetadata) {
|
||||
if (img.format === 'svg') return { src: img.src, srcset: img.src };
|
||||
const widths = [720, 1200, 1600, 2400].filter((w) => w < img.width).concat(img.width);
|
||||
const out = await getImage({ src: img, widths, format: 'webp' });
|
||||
return { src: out.src, srcset: out.srcSet.attribute };
|
||||
}
|
||||
const darkImg = pick('dark');
|
||||
const dark = await sources(darkImg);
|
||||
const light = await sources(pick('light'));
|
||||
---
|
||||
<figure class:list={['shot', className]}>
|
||||
<div class="view">
|
||||
<!-- The theme follows the system, so the browser fetches only the matching variant. -->
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: light)" srcset={light.srcset} sizes={sizes} />
|
||||
<img
|
||||
src={dark.src}
|
||||
srcset={dark.srcset}
|
||||
sizes={sizes}
|
||||
width={darkImg.width}
|
||||
height={darkImg.height}
|
||||
alt={alt ?? spec.alt}
|
||||
loading={priority ? 'eager' : 'lazy'}
|
||||
fetchpriority={priority ? 'high' : undefined}
|
||||
decoding={priority ? 'sync' : 'async'}
|
||||
/>
|
||||
</picture>
|
||||
</div>
|
||||
</figure>
|
||||
<style>
|
||||
.shot {
|
||||
margin: 0; border-radius: 14px; overflow: hidden; background: var(--surface);
|
||||
border: 1px solid var(--line-2); box-shadow: var(--shadow);
|
||||
}
|
||||
.view { aspect-ratio: 16 / 10; background: var(--bg); }
|
||||
.view picture { display: block; width: 100%; height: 100%; }
|
||||
.view img { width: 100%; height: 100%; object-fit: cover; }
|
||||
@media (max-width: 520px) {
|
||||
.shot { border-radius: 10px; }
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user