Skip to content
Merged
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
8 changes: 6 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,12 @@ trust the theme, so a commit message is part of the product.
- **Never narrate the process.** No first-person account of what was tried,
what was missed, or what was learned. "The gate is scoped to the demo
deployment" belongs here; "I only tested two states" does not.
- **No tool or session trailers.** No `Co-Authored-By` for an assistant, and no
links to an AI session. Some tooling adds these by default — remove them.
- **Assistant trailers stay.** A commit written with an assistant ends with a
`Co-Authored-By` line naming it and a `Claude-Session` link to the session
that produced it. The tooling adds both and they are kept, so the authorship
recorded on a commit matches who wrote it. This file asked for them to be
stripped until 2026-09-05, while 218 commits on `main` carried them; the rule
now says what the history does.
- **Present tense, describing the code after the change.** "Scope demo content
to the demo deployment", not "Fixed the demo leaking".
- **The subject names the change; it does not argue for it.** "Rewrite the
Expand Down
Binary file added src/assets/projects/astro-rocket-hero-blue.jpg
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 src/assets/projects/astro-rocket-hero-purple.jpg
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 src/assets/projects/astro-rocket-hero-teal.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
57 changes: 0 additions & 57 deletions src/assets/projects/ecommerce-store-catalog.svg

This file was deleted.

44 changes: 0 additions & 44 deletions src/assets/projects/ecommerce-store-checkout.svg

This file was deleted.

40 changes: 0 additions & 40 deletions src/assets/projects/ecommerce-store-product.svg

This file was deleted.

Binary file not shown.
13 changes: 10 additions & 3 deletions src/components/projects/ProjectCarousel.astro
Original file line number Diff line number Diff line change
Expand Up @@ -111,23 +111,30 @@ const posters = await Promise.all(
</button>

<div class="flex items-center gap-3">
{/* The dot a visitor sees is 8px, which is far below the 24px minimum
a touch target needs. The button carries the 24x24 target and the
span carries the appearance, so the control is thumb-sized without
drawing a thumb-sized dot. The row has no gap: the targets meet
edge to edge rather than overlapping. */}
<div
class="flex items-center gap-2"
class="flex items-center"
role="tablist"
aria-label={t('projects.carouselChooseSlide', locale)}
data-carousel-dots
>
{slides.map((_, i) => (
<button
type="button"
class="carousel-dot h-2 w-2 rounded-full bg-border transition-all hover:bg-brand-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:ring-offset-2 focus-visible:ring-offset-background data-[active=true]:w-6 data-[active=true]:bg-brand-500"
class="carousel-dot group flex h-6 w-6 items-center justify-center rounded-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
data-carousel-dot
data-index={i}
role="tab"
aria-label={`Go to slide ${i + 1}`}
aria-selected={i === 0 ? 'true' : 'false'}
data-active={i === 0 ? 'true' : 'false'}
></button>
>
<span class="h-2 w-2 rounded-full bg-border transition-all group-hover:bg-brand-400 group-data-[active=true]:w-4 group-data-[active=true]:bg-brand-500"></span>
</button>
))}
</div>
<span
Expand Down
22 changes: 14 additions & 8 deletions src/components/projects/ProjectGallery.astro
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,23 @@ const slideData = images.map((item, i) =>

<!-- Dots + counter -->
<div class="mt-4 flex flex-col items-center gap-2">
<div id={`dots-${id}`} class="flex items-center gap-1.5" role="tablist">
{/* As in ProjectCarousel: the button carries a 24x24 touch target and the
span carries the 6px dot, so the control is thumb-sized without the
dot growing. The active state is an attribute rather than a rewritten
class list, so the target sizing cannot be overwritten at runtime. */}
<div id={`dots-${id}`} class="flex items-center" role="tablist">
{images.map((_, i) => (
<button
role="tab"
aria-label={`Go to slide ${i + 1}`}
data-dot={i}
data-dots-gallery={`gallery-${id}`}
class={`h-1.5 rounded-full transition-all duration-300 ${i === 0 ? 'w-5 bg-brand-500' : 'w-1.5 bg-border'}`}
/>
data-active={i === 0 ? 'true' : 'false'}
aria-selected={i === 0 ? 'true' : 'false'}
class="group flex h-6 w-6 items-center justify-center rounded-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
>
<span class="h-1.5 w-1.5 rounded-full bg-border transition-all duration-300 group-data-[active=true]:w-4 group-data-[active=true]:bg-brand-500"></span>
</button>
))}
</div>
<span id={`counter-${id}`} class="text-xs text-foreground-muted">
Expand Down Expand Up @@ -206,11 +214,9 @@ const slideData = images.map((item, i) =>
nextBtn.disabled = index === total - 1;
pauseInactiveVideos(index);
dots.forEach((dot, i) => {
if (i === index) {
dot.className = 'h-1.5 rounded-full transition-all duration-300 w-5 bg-brand-500';
} else {
dot.className = 'h-1.5 rounded-full transition-all duration-300 w-1.5 bg-border';
}
const active = i === index;
dot.setAttribute('data-active', active ? 'true' : 'false');
dot.setAttribute('aria-selected', active ? 'true' : 'false');
});
}

Expand Down
2 changes: 1 addition & 1 deletion src/content/blog/en/project-gallery-video-slides.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,4 @@ ffmpeg -i recording.mov -vf "scale=1280:720" -c:v libx264 -crf 28 \

For reference: the slide union is validated in `src/content.config.ts` (a slide is *either* `src`+`alt` *or* `video`+`poster`+`alt` — anything else fails the build), the shared `GallerySlide` type lives in `src/lib/gallery.ts`, and the rendering happens in `ProjectCarousel.astro` (hero) and `ProjectGallery.astro` (body + lightbox). No new dependencies, no client framework — the same native scroll-snap carousel, with a `<video>` element where a slide asks for one.

`src/content/projects/en/ecommerce-store.mdx` carries a ready-to-uncomment video slide as a starting point. Drop a file in `public/videos/`, point a slide at it, give it a poster — done.
`src/content/projects/en/astro-rocket.mdx` carries a ready-to-uncomment video slide as a starting point. Drop a file in `public/videos/`, point a slide at it, give it a poster — done.
22 changes: 20 additions & 2 deletions src/content/projects/en/astro-rocket.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,26 @@ role: "Designer & Developer"
services: ["Design System", "Component Library", "Open Source"]
meta: ["Free & open source", "MIT licence", "Astro 7", "Lighthouse 4×100"]
icon: "rocket"
image: "../../../assets/projects/hans-martens-dev-astro-rocket.jpg"
imageAlt: "Astro Rocket — my open-source Astro 7 theme, shown on its astrorocket.dev demo."
image: "../../../assets/projects/astro-rocket-hero-blue.jpg"
imageAlt: "The Astro Rocket homepage hero in Blue, the default colour theme."
# Multiple images? Add a `gallery` array and the hero swaps the single
# `image` for a swipeable carousel. The first slide is the lead image.
# (Galleries render on the project detail page, so a project with one has
# to be routable — placeholder projects only appear as cards.)
#
# A slide can also be a self-hosted video: drop the file in `public/`
# and give it a poster image (required — the slide costs zero bytes
# until the visitor presses play). Uncomment the example below to try.
gallery:
# - video: "/videos/demo.mp4"
# poster: "../../../assets/projects/astro-rocket-hero-blue.jpg"
# alt: "Product demo video"
- src: "../../../assets/projects/astro-rocket-hero-blue.jpg"
alt: "The Astro Rocket homepage hero in Blue, the default colour theme."
- src: "../../../assets/projects/astro-rocket-hero-purple.jpg"
alt: "The same homepage hero in the Purple colour theme."
- src: "../../../assets/projects/astro-rocket-hero-teal.jpg"
alt: "The same homepage hero in the Teal colour theme."
---

import LighthouseScoresGrid from '@/components/landing/LighthouseScoresGrid.astro';
Expand Down
Loading
Loading