feat: shuffle hero slideshow projects
All checks were successful
Docker / build-and-push-image (push) Successful in 2m49s

This commit is contained in:
Troy 2025-07-18 23:29:03 +01:00
parent 4e9ffa8771
commit 8e42e17da5
Signed by: troy
GPG key ID: DFC06C02ED3B4711
5 changed files with 82 additions and 69 deletions

99
package-lock.json generated
View file

@ -9,11 +9,11 @@
"version": "0.0.1",
"dependencies": {
"@astrojs/check": "0.9.4",
"@astrojs/mdx": "^4.3.0",
"@astrojs/mdx": "^4.3.1",
"@astrojs/rss": "^4.0.12",
"@astrojs/sitemap": "3.4.1",
"@tailwindcss/vite": "^4.1.11",
"astro": "^5.11.1",
"astro": "^5.12.0",
"astro-icon": "^1.1.5",
"rehype-external-links": "^3.0.0",
"tailwindcss": "^4.1.11",
@ -136,9 +136,9 @@
}
},
"node_modules/@astrojs/markdown-remark": {
"version": "6.3.2",
"resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.2.tgz",
"integrity": "sha512-bO35JbWpVvyKRl7cmSJD822e8YA8ThR/YbUsciWNA7yTcqpIAL2hJDToWP5KcZBWxGT6IOdOkHSXARSNZc4l/Q==",
"version": "6.3.3",
"resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.3.tgz",
"integrity": "sha512-DDRtD1sPvAuA7ms2btc9A7/7DApKqgLMNrE6kh5tmkfy8utD0Z738gqd3p5aViYYdUtHIyEJ1X4mCMxfCfu15w==",
"license": "MIT",
"dependencies": {
"@astrojs/internal-helpers": "0.6.1",
@ -156,7 +156,7 @@
"remark-rehype": "^11.1.2",
"remark-smartypants": "^3.0.2",
"shiki": "^3.2.1",
"smol-toml": "^1.3.1",
"smol-toml": "^1.3.4",
"unified": "^11.0.5",
"unist-util-remove-position": "^5.0.0",
"unist-util-visit": "^5.0.0",
@ -165,12 +165,12 @@
}
},
"node_modules/@astrojs/mdx": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-4.3.0.tgz",
"integrity": "sha512-OGX2KvPeBzjSSKhkCqrUoDMyzFcjKt5nTE5SFw3RdoLf0nrhyCXBQcCyclzWy1+P+XpOamn+p+hm1EhpCRyPxw==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-4.3.1.tgz",
"integrity": "sha512-0ynzkFd5p2IFDLPAfAcGizg44WyS0qUr43nP2vQkvrPlpoPEMeeoi1xWiWsVqQNaZ0FOmNqfUviUn52nm9mLag==",
"license": "MIT",
"dependencies": {
"@astrojs/markdown-remark": "6.3.2",
"@astrojs/markdown-remark": "6.3.3",
"@mdx-js/mdx": "^3.1.0",
"acorn": "^8.14.1",
"es-module-lexer": "^1.6.0",
@ -1624,60 +1624,60 @@
]
},
"node_modules/@shikijs/core": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.4.2.tgz",
"integrity": "sha512-AG8vnSi1W2pbgR2B911EfGqtLE9c4hQBYkv/x7Z+Kt0VxhgQKcW7UNDVYsu9YxwV6u+OJrvdJrMq6DNWoBjihQ==",
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.8.1.tgz",
"integrity": "sha512-uTSXzUBQ/IgFcUa6gmGShCHr4tMdR3pxUiiWKDm8pd42UKJdYhkAYsAmHX5mTwybQ5VyGDgTjW4qKSsRvGSang==",
"license": "MIT",
"dependencies": {
"@shikijs/types": "3.4.2",
"@shikijs/types": "3.8.1",
"@shikijs/vscode-textmate": "^10.0.2",
"@types/hast": "^3.0.4",
"hast-util-to-html": "^9.0.5"
}
},
"node_modules/@shikijs/engine-javascript": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.4.2.tgz",
"integrity": "sha512-1/adJbSMBOkpScCE/SB6XkjJU17ANln3Wky7lOmrnpl+zBdQ1qXUJg2GXTYVHRq+2j3hd1DesmElTXYDgtfSOQ==",
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.8.1.tgz",
"integrity": "sha512-rZRp3BM1llrHkuBPAdYAzjlF7OqlM0rm/7EWASeCcY7cRYZIrOnGIHE9qsLz5TCjGefxBFnwgIECzBs2vmOyKA==",
"license": "MIT",
"dependencies": {
"@shikijs/types": "3.4.2",
"@shikijs/types": "3.8.1",
"@shikijs/vscode-textmate": "^10.0.2",
"oniguruma-to-es": "^4.3.3"
}
},
"node_modules/@shikijs/engine-oniguruma": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.4.2.tgz",
"integrity": "sha512-zcZKMnNndgRa3ORja6Iemsr3DrLtkX3cAF7lTJkdMB6v9alhlBsX9uNiCpqofNrXOvpA3h6lHcLJxgCIhVOU5Q==",
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.8.1.tgz",
"integrity": "sha512-KGQJZHlNY7c656qPFEQpIoqOuC4LrxjyNndRdzk5WKB/Ie87+NJCF1xo9KkOUxwxylk7rT6nhlZyTGTC4fCe1g==",
"license": "MIT",
"dependencies": {
"@shikijs/types": "3.4.2",
"@shikijs/types": "3.8.1",
"@shikijs/vscode-textmate": "^10.0.2"
}
},
"node_modules/@shikijs/langs": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.4.2.tgz",
"integrity": "sha512-H6azIAM+OXD98yztIfs/KH5H4PU39t+SREhmM8LaNXyUrqj2mx+zVkr8MWYqjceSjDw9I1jawm1WdFqU806rMA==",
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.8.1.tgz",
"integrity": "sha512-TjOFg2Wp1w07oKnXjs0AUMb4kJvujML+fJ1C5cmEj45lhjbUXtziT1x2bPQb9Db6kmPhkG5NI2tgYW1/DzhUuQ==",
"license": "MIT",
"dependencies": {
"@shikijs/types": "3.4.2"
"@shikijs/types": "3.8.1"
}
},
"node_modules/@shikijs/themes": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.4.2.tgz",
"integrity": "sha512-qAEuAQh+brd8Jyej2UDDf+b4V2g1Rm8aBIdvt32XhDPrHvDkEnpb7Kzc9hSuHUxz0Iuflmq7elaDuQAP9bHIhg==",
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.8.1.tgz",
"integrity": "sha512-Vu3t3BBLifc0GB0UPg2Pox1naTemrrvyZv2lkiSw3QayVV60me1ujFQwPZGgUTmwXl1yhCPW8Lieesm0CYruLQ==",
"license": "MIT",
"dependencies": {
"@shikijs/types": "3.4.2"
"@shikijs/types": "3.8.1"
}
},
"node_modules/@shikijs/types": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.4.2.tgz",
"integrity": "sha512-zHC1l7L+eQlDXLnxvM9R91Efh2V4+rN3oMVS2swCBssbj2U/FBwybD1eeLaq8yl/iwT+zih8iUbTBCgGZOYlVg==",
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.8.1.tgz",
"integrity": "sha512-5C39Q8/8r1I26suLh+5TPk1DTrbY/kn3IdWA5HdizR0FhlhD05zx5nKCqhzSfDHH3p4S0ZefxWd77DLV+8FhGg==",
"license": "MIT",
"dependencies": {
"@shikijs/vscode-textmate": "^10.0.2",
@ -2455,14 +2455,14 @@
}
},
"node_modules/astro": {
"version": "5.11.1",
"resolved": "https://registry.npmjs.org/astro/-/astro-5.11.1.tgz",
"integrity": "sha512-32dpUh0tXSV/FR2q2/z7LOA6IXl7RqET9J51IA0pPSSi3exhRP3EOSQGjBq10DzXT7VrvplDrFqwfiiWBS8oYA==",
"version": "5.12.0",
"resolved": "https://registry.npmjs.org/astro/-/astro-5.12.0.tgz",
"integrity": "sha512-Oov5JsMFHuUmuO+Nx6plfv3nQNK1Xl/8CgLvR8lBhZTjYnraxhuPX5COVAzbom+YLgwaDfK7KBd8zOEopRf9mg==",
"license": "MIT",
"dependencies": {
"@astrojs/compiler": "^2.12.2",
"@astrojs/internal-helpers": "0.6.1",
"@astrojs/markdown-remark": "6.3.2",
"@astrojs/markdown-remark": "6.3.3",
"@astrojs/telemetry": "3.3.0",
"@capsizecss/unpack": "^2.4.0",
"@oslojs/encoding": "^1.1.0",
@ -2505,6 +2505,7 @@
"rehype": "^13.0.2",
"semver": "^7.7.1",
"shiki": "^3.2.1",
"smol-toml": "^1.3.4",
"tinyexec": "^0.3.2",
"tinyglobby": "^0.2.12",
"tsconfck": "^3.1.5",
@ -7295,17 +7296,17 @@
}
},
"node_modules/shiki": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-3.4.2.tgz",
"integrity": "sha512-wuxzZzQG8kvZndD7nustrNFIKYJ1jJoWIPaBpVe2+KHSvtzMi4SBjOxrigs8qeqce/l3U0cwiC+VAkLKSunHQQ==",
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-3.8.1.tgz",
"integrity": "sha512-+MYIyjwGPCaegbpBeFN9+oOifI8CKiKG3awI/6h3JeT85c//H2wDW/xCJEGuQ5jPqtbboKNqNy+JyX9PYpGwNg==",
"license": "MIT",
"dependencies": {
"@shikijs/core": "3.4.2",
"@shikijs/engine-javascript": "3.4.2",
"@shikijs/engine-oniguruma": "3.4.2",
"@shikijs/langs": "3.4.2",
"@shikijs/themes": "3.4.2",
"@shikijs/types": "3.4.2",
"@shikijs/core": "3.8.1",
"@shikijs/engine-javascript": "3.8.1",
"@shikijs/engine-oniguruma": "3.8.1",
"@shikijs/langs": "3.8.1",
"@shikijs/themes": "3.8.1",
"@shikijs/types": "3.8.1",
"@shikijs/vscode-textmate": "^10.0.2",
"@types/hast": "^3.0.4"
}
@ -7352,9 +7353,9 @@
"license": "MIT"
},
"node_modules/smol-toml": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.4.tgz",
"integrity": "sha512-UOPtVuYkzYGee0Bd2Szz8d2G3RfMfJ2t3qVdZUAozZyAk+a0Sxa+QKix0YCwjL/A1RR0ar44nCxaoN9FxdJGwA==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.4.1.tgz",
"integrity": "sha512-CxdwHXyYTONGHThDbq5XdwbFsuY4wlClRGejfE2NtwUtiHYsP1QtNsHb/hnj31jKYSchztJsaA8pSQoVzkfCFg==",
"license": "BSD-3-Clause",
"engines": {
"node": ">= 18"

View file

@ -13,11 +13,11 @@
},
"dependencies": {
"@astrojs/check": "0.9.4",
"@astrojs/mdx": "^4.3.0",
"@astrojs/mdx": "^4.3.1",
"@astrojs/rss": "^4.0.12",
"@astrojs/sitemap": "3.4.1",
"@tailwindcss/vite": "^4.1.11",
"astro": "^5.11.1",
"astro": "^5.12.0",
"astro-icon": "^1.1.5",
"rehype-external-links": "^3.0.0",
"tailwindcss": "^4.1.11",

View file

@ -10,13 +10,13 @@
>
Im a recent <a
href="/about#education"
class="text-secondary underline hover:no-underline"
class="text-secondary decoration-tertiary underline hover:no-underline"
>Game Arts and Design</a
> graduate with a chosen area of focus on the design, lighting, and rendering
of 3D environments. On the side I manage online operations for a family run
<a
href="/projects/camouflage-store"
class="text-secondary underline hover:no-underline"
class="text-secondary decoration-tertiary underline hover:no-underline"
>outdoor apparel business</a
>.
</p>

View file

@ -43,21 +43,33 @@ const { interval = 3000, images } = Astro.props;
</div>
<script>
const images = document.querySelectorAll<HTMLElement>("[data-slide-index]");
const images = Array.from(
document.querySelectorAll<HTMLElement>("[data-slide-index]"),
);
let currentImageIndex = 0;
const interval =
Number(
(
document
const closestRelativeElement = document
.querySelector<HTMLElement>("[data-slide-index]")
?.closest(".relative") as HTMLElement
)?.dataset?.interval,
) || 3000;
?.closest(".relative") as HTMLElement | null;
const interval = Number(closestRelativeElement?.dataset?.interval) || 3000;
for (let i = images.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[images[i], images[j]] = [images[j], images[i]];
}
images.forEach((img, index) => {
img.style.opacity = index === 0 ? "1" : "0";
img.style.zIndex = index === 0 ? "10" : "1";
});
function showNextImage() {
images[currentImageIndex].style.opacity = "0";
images[currentImageIndex].style.zIndex = "1";
currentImageIndex = (currentImageIndex + 1) % images.length;
images[currentImageIndex].style.opacity = "1";
images[currentImageIndex].style.zIndex = "10";
}

View file

@ -158,7 +158,7 @@ const sortedEducation = [...education].sort((a, b) => a.id - b.id);
</h1>
<p class="text-secondary/70 max-w-md text-pretty">
<Link
class="inline-flex items-center gap-x-1.5 align-baseline leading-none hover:underline hover:decoration-2 hover:underline-offset-2"
class="decoration-tertiary inline-flex items-center gap-x-1.5 align-baseline leading-none hover:underline hover:decoration-2 hover:underline-offset-2"
href="https://www.google.com/maps/place/Devon"
>
<Icon name="mdi:earth" class="h-3 w-3" />
@ -221,7 +221,7 @@ const sortedEducation = [...education].sort((a, b) => a.id - b.id);
<p class="text-secondary/70 text-lg font-medium">
Im a recent <a
href="#education"
class="text-secondary underline hover:no-underline"
class="text-secondary decoration-tertiary underline hover:no-underline"
>Game Arts and Design</a
> graduate with a chosen area of focus on the design, lighting, and rendering
of 3D environments. Using either real-time or offline rendering techniques
@ -230,7 +230,7 @@ const sortedEducation = [...education].sort((a, b) => a.id - b.id);
and cyber security in addition to motorbike touring. An up-to-date portfolio
of my work can be found on my website at <a
href="/projects"
class="text-secondary underline hover:no-underline"
class="text-secondary decoration-tertiary underline hover:no-underline"
>troylusty.com/projects</a
>.
</p>
@ -248,7 +248,7 @@ const sortedEducation = [...education].sort((a, b) => a.id - b.id);
Do you think I'd be a good fit to help out on a project you're working
on, or maybe just want to chat? <Link
href={`mailto:${SITE.EMAIL}`}
class="underline hover:no-underline"
class="decoration-tertiary underline hover:no-underline"
>
Send me an email!</Link
>
@ -339,7 +339,7 @@ const sortedEducation = [...education].sort((a, b) => a.id - b.id);
{experience.link ? (
<Link
href={experience.link}
class="text-secondary/70 underline hover:no-underline"
class="text-secondary/70 decoration-tertiary underline hover:no-underline"
>
{experience.name}
</Link>
@ -372,7 +372,7 @@ const sortedEducation = [...education].sort((a, b) => a.id - b.id);
<h3 class="mt-0 text-base font-semibold tracking-tight">
<Link
href={project.link}
class="inline-flex items-center gap-1 font-medium underline hover:no-underline"
class="decoration-tertiary inline-flex items-center gap-1 font-medium underline hover:no-underline"
>
{project.done ? (
<span class="mr-1 h-1.5 w-1.5 rounded-full bg-green-600 dark:bg-green-400" />
@ -394,7 +394,7 @@ const sortedEducation = [...education].sort((a, b) => a.id - b.id);
{project.tags.map((tag) => (
<a
href={`/tags/${createSlug(tag)}`}
class="bg-button text-secondary hover:bg-button-active flex w-fit flex-row items-center gap-1 justify-self-center rounded-sm px-2 py-1 text-center font-sans text-xs font-light text-nowrap capitalize no-underline transition-colors duration-300"
class="bg-button text-secondary hover:bg-button-active decoration-tertiary flex w-fit flex-row items-center gap-1 justify-self-center rounded-sm px-2 py-1 text-center font-sans text-xs font-light text-nowrap capitalize no-underline transition-colors duration-300"
>
{tag}
</a>