Stop reading clutter
This commit is contained in:
parent
82c723c192
commit
1cdc202731
8 changed files with 11 additions and 67 deletions
|
|
@ -1,13 +0,0 @@
|
||||||
WEBVTT
|
|
||||||
|
|
||||||
00:00.000 --> 00:04.000
|
|
||||||
No spoken dialogue. Game audio only.
|
|
||||||
|
|
||||||
00:04.000 --> 00:35.000
|
|
||||||
The Ad Astra handheld board runs the game on a small OLED display.
|
|
||||||
|
|
||||||
00:35.000 --> 01:05.000
|
|
||||||
The player controls the game through the IR input while the engine updates the display in real time.
|
|
||||||
|
|
||||||
01:05.000 --> 01:34.600
|
|
||||||
The clip continues showing gameplay on the custom ATtiny85-based board.
|
|
||||||
|
|
@ -5,7 +5,6 @@ import ProjectLinks from './ProjectLinks.astro';
|
||||||
type Link = CollectionEntry<'work'>['data']['links'][number];
|
type Link = CollectionEntry<'work'>['data']['links'][number];
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
role?: string;
|
|
||||||
projectPeriod?: string;
|
projectPeriod?: string;
|
||||||
stack?: string[];
|
stack?: string[];
|
||||||
scale?: string;
|
scale?: string;
|
||||||
|
|
@ -14,18 +13,9 @@ interface Props {
|
||||||
headingId: string;
|
headingId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const { projectPeriod, stack = [], scale, outcome, links = [], headingId } = Astro.props;
|
||||||
role,
|
|
||||||
projectPeriod,
|
|
||||||
stack = [],
|
|
||||||
scale,
|
|
||||||
outcome,
|
|
||||||
links = [],
|
|
||||||
headingId,
|
|
||||||
} = Astro.props;
|
|
||||||
|
|
||||||
const rows: Array<[string, string]> = [];
|
const rows: Array<[string, string]> = [];
|
||||||
if (role) rows.push(['Role', role]);
|
|
||||||
if (projectPeriod) rows.push(['Period', projectPeriod]);
|
if (projectPeriod) rows.push(['Period', projectPeriod]);
|
||||||
if (stack.length > 0) rows.push(['Stack', stack.join(', ')]);
|
if (stack.length > 0) rows.push(['Stack', stack.join(', ')]);
|
||||||
if (scale) rows.push(['Scale', scale]);
|
if (scale) rows.push(['Scale', scale]);
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ const videoHeight = item.type === 'video' ? (item.poster?.height ?? 720) : undef
|
||||||
{
|
{
|
||||||
item.type === 'video' ? (
|
item.type === 'video' ? (
|
||||||
// Decorative videos stay inert and hidden from assistive tech. Meaningful
|
// Decorative videos stay inert and hidden from assistive tech. Meaningful
|
||||||
// videos expose controls, captions, and an accessible name.
|
// videos expose controls and an accessible name.
|
||||||
item.decorative ? (
|
item.decorative ? (
|
||||||
<video
|
<video
|
||||||
muted
|
muted
|
||||||
|
|
@ -44,15 +44,6 @@ const videoHeight = item.type === 'video' ? (item.poster?.height ?? 720) : undef
|
||||||
>
|
>
|
||||||
{item.webm && <source src={item.webm} type="video/webm" />}
|
{item.webm && <source src={item.webm} type="video/webm" />}
|
||||||
{item.mp4 && <source src={item.mp4} type="video/mp4" />}
|
{item.mp4 && <source src={item.mp4} type="video/mp4" />}
|
||||||
{item.captions && (
|
|
||||||
<track
|
|
||||||
kind="captions"
|
|
||||||
src={item.captions}
|
|
||||||
srclang="en"
|
|
||||||
label={item.captionsLabel}
|
|
||||||
default
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</video>
|
</video>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -111,17 +111,6 @@ const playScript = `
|
||||||
>
|
>
|
||||||
{video.webm && <source src={video.webm} type="video/webm" />}
|
{video.webm && <source src={video.webm} type="video/webm" />}
|
||||||
{video.mp4 && <source src={video.mp4} type="video/mp4" />}
|
{video.mp4 && <source src={video.mp4} type="video/mp4" />}
|
||||||
{
|
|
||||||
video.captions && (
|
|
||||||
<track
|
|
||||||
kind="captions"
|
|
||||||
src={video.captions}
|
|
||||||
srclang="en"
|
|
||||||
label={video.captionsLabel}
|
|
||||||
default
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</video>
|
</video>
|
||||||
|
|
||||||
<noscript>
|
<noscript>
|
||||||
|
|
|
||||||
|
|
@ -64,13 +64,12 @@ const thumbnailSchema = ({ image }: SchemaContext) =>
|
||||||
alt: z.string().min(1, 'Thumbnail alt text must not be empty.'),
|
alt: z.string().min(1, 'Thumbnail alt text must not be empty.'),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Partial thumbnail used by the project card to override just the image, just
|
// Thumbnail override used by the project card to swap just the image when the
|
||||||
// the alt text, or both. Anything omitted falls back to the entry's top-level
|
// card wants a different crop from the article. The alt text is never
|
||||||
// thumbnail, so a card that only rewords its alt doesn't repeat the `src`.
|
// overridden, so it stays a single source of truth on the top-level thumbnail.
|
||||||
const thumbnailOverrideSchema = ({ image }: SchemaContext) =>
|
const thumbnailOverrideSchema = ({ image }: SchemaContext) =>
|
||||||
z.object({
|
z.object({
|
||||||
src: image().optional(),
|
src: image(),
|
||||||
alt: z.string().min(1, 'Thumbnail alt text must not be empty.').optional(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const mediaSchema = ({ image }: SchemaContext) =>
|
const mediaSchema = ({ image }: SchemaContext) =>
|
||||||
|
|
@ -90,8 +89,6 @@ const mediaSchema = ({ image }: SchemaContext) =>
|
||||||
poster: image().optional(),
|
poster: image().optional(),
|
||||||
mp4: mediaUrl.optional(),
|
mp4: mediaUrl.optional(),
|
||||||
webm: mediaUrl.optional(),
|
webm: mediaUrl.optional(),
|
||||||
captions: mediaUrl.optional(),
|
|
||||||
captionsLabel: z.string().default('English captions'),
|
|
||||||
alt: z.string().optional(),
|
alt: z.string().optional(),
|
||||||
decorative: z.boolean().optional(),
|
decorative: z.boolean().optional(),
|
||||||
caption: z.string().optional(),
|
caption: z.string().optional(),
|
||||||
|
|
@ -104,12 +101,6 @@ const mediaSchema = ({ image }: SchemaContext) =>
|
||||||
.refine((item) => item.decorative || (Boolean(item.alt) && Boolean(item.caption)), {
|
.refine((item) => item.decorative || (Boolean(item.alt) && Boolean(item.caption)), {
|
||||||
message: 'Meaningful media needs both alt text and a caption.',
|
message: 'Meaningful media needs both alt text and a caption.',
|
||||||
})
|
})
|
||||||
.refine(
|
|
||||||
(item) => item.type !== 'video' || item.decorative || Boolean(item.captions),
|
|
||||||
{
|
|
||||||
message: 'Meaningful video needs captions.',
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.refine(
|
.refine(
|
||||||
(item) => item.type !== 'video' || item.decorative || Boolean(item.transcript),
|
(item) => item.type !== 'video' || item.decorative || Boolean(item.transcript),
|
||||||
{
|
{
|
||||||
|
|
@ -130,11 +121,9 @@ const articleFacet = ({ image }: SchemaContext) =>
|
||||||
iframeThumbnail: z.boolean().default(false),
|
iframeThumbnail: z.boolean().default(false),
|
||||||
featuredOrder: z.number().optional(),
|
featuredOrder: z.number().optional(),
|
||||||
tags: z.array(z.enum(TAGS)).default([]),
|
tags: z.array(z.enum(TAGS)).default([]),
|
||||||
role: z.string().min(1).optional(),
|
|
||||||
stack: z.array(z.string()).optional(),
|
stack: z.array(z.string()).optional(),
|
||||||
scale: z.string().min(1).optional(),
|
scale: z.string().min(1).optional(),
|
||||||
outcome: z.string().min(1).optional(),
|
outcome: z.string().min(1).optional(),
|
||||||
audience: z.enum(['general', 'technical', 'recruiter-relevant']).default('technical'),
|
|
||||||
media: z.array(mediaSchema({ image })).default([]),
|
media: z.array(mediaSchema({ image })).default([]),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -145,7 +134,6 @@ const projectFacet = ({ image }: SchemaContext) =>
|
||||||
technologies: z.array(z.string()).optional(),
|
technologies: z.array(z.string()).optional(),
|
||||||
// Card overrides. When omitted, the card uses the top-level value.
|
// Card overrides. When omitted, the card uses the top-level value.
|
||||||
title: z.string().optional(),
|
title: z.string().optional(),
|
||||||
description: z.string().max(160).optional(),
|
|
||||||
thumbnail: thumbnailOverrideSchema({ image }).optional(),
|
thumbnail: thumbnailOverrideSchema({ image }).optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,6 @@ const personJsonLd = buildPersonJsonLd();
|
||||||
|
|
||||||
<AtAGlance
|
<AtAGlance
|
||||||
headingId={`at-a-glance-${post.id}`}
|
headingId={`at-a-glance-${post.id}`}
|
||||||
role={post.data.article?.role}
|
|
||||||
projectPeriod={post.data.period}
|
projectPeriod={post.data.period}
|
||||||
stack={post.data.article?.stack}
|
stack={post.data.article?.stack}
|
||||||
scale={post.data.article?.scale}
|
scale={post.data.article?.scale}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ import type { ImageMetadata } from 'astro';
|
||||||
|
|
||||||
// Theme background colors, the single source of truth for the
|
// Theme background colors, the single source of truth for the
|
||||||
// <meta name="theme-color"> tags and the FOUC-prevention scripts (injected in
|
// <meta name="theme-color"> tags and the FOUC-prevention scripts (injected in
|
||||||
// Base.astro and Header.astro). Keep --color-bg in global.css in sync; CSS
|
// Base.astro and Header.astro). Keep --color-bg in styles/tokens.css in sync;
|
||||||
// cannot import these values.
|
// CSS cannot import these values.
|
||||||
export const THEME_BG = { light: '#fbfaf7', dark: '#201f1d' };
|
export const THEME_BG = { light: '#fbfaf7', dark: '#201f1d' };
|
||||||
|
|
||||||
export const site = {
|
export const site = {
|
||||||
|
|
@ -139,10 +139,10 @@ export function projectCard(entry: CollectionEntry<'work'>): ProjectCard {
|
||||||
const project = data.project;
|
const project = data.project;
|
||||||
return {
|
return {
|
||||||
title: project?.title ?? data.title,
|
title: project?.title ?? data.title,
|
||||||
description: project?.description ?? data.description,
|
description: data.description,
|
||||||
thumbnail: {
|
thumbnail: {
|
||||||
src: project?.thumbnail?.src ?? data.thumbnail.src,
|
src: project?.thumbnail?.src ?? data.thumbnail.src,
|
||||||
alt: project?.thumbnail?.alt ?? data.thumbnail.alt,
|
alt: data.thumbnail.alt,
|
||||||
},
|
},
|
||||||
technologies: project?.technologies ?? data.article?.stack ?? [],
|
technologies: project?.technologies ?? data.article?.stack ?? [],
|
||||||
selected: project?.selected ?? false,
|
selected: project?.selected ?? false,
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ const STARTING_POINTS = 5;
|
||||||
|
|
||||||
const posts = await getArticles();
|
const posts = await getArticles();
|
||||||
const startingPoints = posts
|
const startingPoints = posts
|
||||||
.filter((post) => post.data.article?.audience === 'recruiter-relevant')
|
.filter((post) => post.data.article?.featuredOrder !== undefined)
|
||||||
.sort(
|
.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
(a.data.article?.featuredOrder ?? 99) - (b.data.article?.featuredOrder ?? 99)
|
(a.data.article?.featuredOrder ?? 99) - (b.data.article?.featuredOrder ?? 99)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue