Tidy up metadata, headings, forms, and small accessibility fixes
Build and Deploy to Web Server / deploy (push) Successful in 4m34s
Build and Deploy to Web Server / deploy (push) Successful in 4m34s
This commit is contained in:
18 files changed
+107
-59
No files matched your search
@@ -1,7 +1,11 @@
|
||||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
|
||||
const ID = `contact-form-${Math.random().toString(36)}`;
|
||||
interface Props {
|
||||
id?: string;
|
||||
}
|
||||
|
||||
const { id: ID = "contact-form" } = Astro.props;
|
||||
---
|
||||
|
||||
<form
|
||||
@@ -9,60 +13,78 @@ const ID = `contact-form-${Math.random().toString(36)}`;
|
||||
id={ID}
|
||||
>
|
||||
<div>
|
||||
<label for="name" class="block font-bold">Name</label>
|
||||
<label for={`${ID}-name`} class="block font-bold">Name</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
id={`${ID}-name`}
|
||||
name="name"
|
||||
required
|
||||
autocomplete="name"
|
||||
maxlength="100"
|
||||
class="mt-2 block w-full rounded p-2 shadow dark:bg-gray-600"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="email" class="block font-bold">Your email</label>
|
||||
<label for={`${ID}-email`} class="block font-bold">Your email</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
id={`${ID}-email`}
|
||||
name="email"
|
||||
required
|
||||
autocomplete="email"
|
||||
maxlength="254"
|
||||
class="mt-2 block w-full rounded p-2 shadow dark:bg-gray-600"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="subject" class="block font-bold">Subject</label>
|
||||
<label for={`${ID}-subject`} class="block font-bold">Subject</label>
|
||||
<input
|
||||
type="text"
|
||||
id="subject"
|
||||
id={`${ID}-subject`}
|
||||
name="subject"
|
||||
required
|
||||
maxlength="150"
|
||||
class="mt-2 block w-full rounded p-2 shadow dark:bg-gray-600"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="message" class="block font-bold">Message</label>
|
||||
<label for={`${ID}-message`} class="block font-bold">Message</label>
|
||||
<textarea
|
||||
id="message"
|
||||
id={`${ID}-message`}
|
||||
name="message"
|
||||
rows="4"
|
||||
required
|
||||
maxlength="5000"
|
||||
class="mt-2 block w-full rounded p-2 shadow dark:bg-gray-600"></textarea>
|
||||
</div>
|
||||
<button
|
||||
id="submit"
|
||||
id={`${ID}-submit`}
|
||||
type="submit"
|
||||
class="bg-primary text-md font-header hover:text-primary repeat hover:ring-primary mx-auto inline-block rounded px-6 py-3 font-light text-white uppercase drop-shadow transition hover:bg-white hover:ring-2"
|
||||
class="bg-primary text-md font-header hover:text-primary hover:ring-primary mx-auto inline-block rounded px-6 py-3 font-light text-white uppercase drop-shadow transition hover:bg-white hover:ring-2"
|
||||
><Icon
|
||||
name="mdi:loading"
|
||||
id="loading-icon"
|
||||
id={`${ID}-loading-icon`}
|
||||
class="mr-4 hidden animate-spin"
|
||||
/><span id="response-message" class="inline">Send message</span></button
|
||||
/><span
|
||||
id={`${ID}-response-message`}
|
||||
class="inline"
|
||||
role="status"
|
||||
aria-live="polite">Send message</span
|
||||
></button
|
||||
>
|
||||
</form>
|
||||
|
||||
<script define:vars={{ ID }}>
|
||||
function init() {
|
||||
const form = document.getElementById(ID);
|
||||
const responseMsg = form.querySelector("#response-message");
|
||||
const button = form.querySelector("#submit");
|
||||
const loadingIcon = form.querySelector("#loading-icon");
|
||||
if (!form) return;
|
||||
const responseMsg = form.querySelector(
|
||||
`#${CSS.escape(ID + "-response-message")}`
|
||||
);
|
||||
const button = form.querySelector(`#${CSS.escape(ID + "-submit")}`);
|
||||
const loadingIcon = form.querySelector(
|
||||
`#${CSS.escape(ID + "-loading-icon")}`
|
||||
);
|
||||
|
||||
form.addEventListener("submit", async function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
@@ -16,16 +16,19 @@ const imagesArray = await Promise.all(
|
||||
<div class="absolute -z-50 h-full w-full bg-black"></div>
|
||||
<img
|
||||
src={imagesArray[0].default.src}
|
||||
alt=""
|
||||
class:list={[
|
||||
"object-no-repeat absolute -z-50 h-full w-full object-cover object-center transition duration-2000",
|
||||
"absolute -z-50 h-full w-full object-cover object-center transition duration-2000",
|
||||
transitionStyle
|
||||
]}
|
||||
id="hero-image-1"
|
||||
/>
|
||||
<img
|
||||
src={imagesArray[1].default.src}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
class:list={[
|
||||
"object-no-repeat absolute -z-50 h-full w-full object-cover object-center transition duration-2000",
|
||||
"absolute -z-50 h-full w-full object-cover object-center transition duration-2000",
|
||||
transitionStyle
|
||||
]}
|
||||
id="hero-image-2"
|
||||
|
||||
@@ -62,7 +62,13 @@ const { navbarDisplay = "normal" } = Astro.props as Props;
|
||||
|
||||
<!-- Mobile Menu Button -->
|
||||
<div class="md:hidden">
|
||||
<button id="menu-toggle" class="text-2xl focus:outline-none">
|
||||
<button
|
||||
id="menu-toggle"
|
||||
class="text-2xl focus:outline-none"
|
||||
aria-label="Toggle navigation menu"
|
||||
aria-expanded="false"
|
||||
aria-controls="mobile-menu"
|
||||
>
|
||||
☰
|
||||
</button>
|
||||
</div>
|
||||
@@ -100,10 +106,7 @@ const { navbarDisplay = "normal" } = Astro.props as Props;
|
||||
|
||||
<!-- Extra spacing for non-transparent navbar -->
|
||||
<div
|
||||
class:list={[
|
||||
"y-0 r-0 h-12",
|
||||
navbarDisplay === "transparent" ? "hidden" : "block"
|
||||
]}
|
||||
class:list={["h-12", navbarDisplay === "transparent" ? "hidden" : "block"]}
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -141,12 +144,23 @@ const { navbarDisplay = "normal" } = Astro.props as Props;
|
||||
if (nav) {
|
||||
const visible = nav.getAttribute("data-mobile-menu") === "visible";
|
||||
nav.setAttribute("data-mobile-menu", visible ? "hidden" : "visible");
|
||||
toggle.setAttribute("aria-expanded", visible ? "false" : "true");
|
||||
}
|
||||
|
||||
checkScroll();
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (
|
||||
e.key === "Escape" &&
|
||||
nav?.getAttribute("data-mobile-menu") === "visible"
|
||||
) {
|
||||
nav.setAttribute("data-mobile-menu", "hidden");
|
||||
toggle?.setAttribute("aria-expanded", "false");
|
||||
}
|
||||
});
|
||||
|
||||
checkScroll();
|
||||
});
|
||||
|
||||
|
||||
@@ -399,7 +399,10 @@ const playerArtworkOpacity = 0.4;
|
||||
|
||||
this.UI.progress.bar.addEventListener("click", function (event) {
|
||||
event.stopPropagation();
|
||||
window.player.seek(event.clientX / window.innerWidth);
|
||||
const rect = this.getBoundingClientRect();
|
||||
const per =
|
||||
rect.width > 0 ? (event.clientX - rect.left) / rect.width : 0;
|
||||
window.player.seek(Math.min(Math.max(per, 0), 1));
|
||||
});
|
||||
|
||||
// Tap the circle to expand (not the play button).
|
||||
@@ -578,8 +581,9 @@ const playerArtworkOpacity = 0.4;
|
||||
*/
|
||||
pause() {
|
||||
// Get the Howl we want to manipulate.
|
||||
var sound = this.playlist[this.index].howl;
|
||||
var sound = this.playlist[this.index]?.howl;
|
||||
|
||||
if (!sound) return;
|
||||
sound.pause();
|
||||
|
||||
this.syncPlayPauseUI(false);
|
||||
|
||||
@@ -16,7 +16,7 @@ import { Schema } from "astro-seo-schema";
|
||||
|
||||
<!-- SEO -->
|
||||
<link rel="canonical" href={url} />
|
||||
<meta name="robots" content="all" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<Schema
|
||||
item={{
|
||||
"@context": "https://schema.org",
|
||||
@@ -86,8 +86,11 @@ import { Schema } from "astro-seo-schema";
|
||||
/>
|
||||
<meta property="og:image:alt" content={image?.alt || site.image.alt} />
|
||||
<meta property="og:image:type" content="image/jpeg" />
|
||||
<meta property="og:image:width" content={image?.width.toString() || "1920"} />
|
||||
<meta property="og:image:height" content={image?.height.toString() || "1080"} />
|
||||
<meta property="og:image:width" content={image?.width?.toString() || "1920"} />
|
||||
<meta
|
||||
property="og:image:height"
|
||||
content={image?.height?.toString() || "1080"}
|
||||
/>
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:site" content="@nathancummins.au" />
|
||||
@@ -100,8 +103,8 @@ import { Schema } from "astro-seo-schema";
|
||||
? image.externalURL
|
||||
: site.image.externalURL}
|
||||
/>
|
||||
<meta name="twitter:image:alt" content={title} />
|
||||
<meta name="twitter:domain" content={import.meta.env.SITE} />
|
||||
|
||||
<!-- Theming -->
|
||||
<meta name="theme-color" content="#ff2d00" />
|
||||
<meta name="twitter:image:alt" content={image?.alt || site.image.alt} />
|
||||
<meta
|
||||
name="twitter:domain"
|
||||
content={import.meta.env.SITE ?? "www.nathancummins.com.au"}
|
||||
/>
|
||||
@@ -7,7 +7,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const {
|
||||
Tag = "h1",
|
||||
Tag = "h2",
|
||||
lineColour = "border-primary",
|
||||
lineColourDark = "dark:text-primary",
|
||||
class: className
|
||||
|
||||
@@ -26,13 +26,13 @@ const linkIsExternal: boolean =
|
||||
target={href?.toString().startsWith("http") ? "_blank" : undefined}
|
||||
rel={linkIsExternal ? "noopener noreferrer" : undefined}
|
||||
{...attrs}
|
||||
><span class="group-hover:text-primary-100"><slot /></span>{
|
||||
><span class="group-hover:text-primary-600"><slot /></span>{
|
||||
includeExternalLinkIcon && linkIsExternal && (
|
||||
<sup class="ml-0.5 inline-block">
|
||||
<Icon
|
||||
name="mdi:external-link"
|
||||
class:list={[
|
||||
"text-primary group-hover:text-primary-100 m-0 inline-block"
|
||||
"text-primary group-hover:text-primary-600 m-0 inline-block"
|
||||
]}
|
||||
/>
|
||||
</sup>
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
import { Icon } from "astro-icon/components";
|
||||
---
|
||||
|
||||
<span
|
||||
class="theme:switcher flex justify-center text-center align-middle text-2xl"
|
||||
<button
|
||||
type="button"
|
||||
class="theme:switcher mx-auto flex w-full justify-center text-center align-middle text-2xl"
|
||||
onclick="toggleTheme()"
|
||||
aria-label="Toggle colour theme"
|
||||
>
|
||||
<span class="theme:light-mode">
|
||||
<Icon name="fa7-solid:sun" />
|
||||
@@ -12,7 +14,7 @@ import { Icon } from "astro-icon/components";
|
||||
<span class="theme:dark-mode">
|
||||
<Icon name="fa7-solid:moon" />
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<script is:inline>
|
||||
function updateThemeIconAndAriaLabel(
|
||||
|
||||
+1
-1
@@ -8,6 +8,6 @@ export default {
|
||||
image: {
|
||||
externalURL: `${import.meta.env.SITE}/nathan-cummins-composer-orchestrator-conductor.jpg`,
|
||||
src: "nathan-cummins-composer-orchestrator-conductor.jpg",
|
||||
alt: "Nathan Cummins"
|
||||
alt: "Picture of Nathan Cummins, Adelaide composer, orchestrator, and conductor"
|
||||
} as SiteImage
|
||||
};
|
||||
Vendored
+1
@@ -14,6 +14,7 @@ interface Window {
|
||||
}
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly SITE?: string;
|
||||
readonly EMAIL_USER: string;
|
||||
readonly EMAIL_PASS: string;
|
||||
readonly EMAIL_PORT: number;
|
||||
|
||||
+5
-4
@@ -17,7 +17,7 @@ const allProjectHeros = import.meta.glob<{ default: ImageMetadata }>(
|
||||
);
|
||||
|
||||
const allImages = import.meta.glob<{ default: ImageMetadata }>(
|
||||
"/**/*.{jpeg,jpg,png,gif}",
|
||||
"/src/assets/**/*.{jpeg,jpg,png,gif}",
|
||||
{
|
||||
eager: true
|
||||
}
|
||||
@@ -82,11 +82,12 @@ export function getAllProjectImages(project: Project): ImageMetadata[] {
|
||||
}
|
||||
|
||||
export function shuffleArray<T>(array: Array<T>): Array<T> {
|
||||
for (let i = array.length - 1; i > 0; i--) {
|
||||
const copy = [...array];
|
||||
for (let i = copy.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[array[i], array[j]] = [array[j], array[i]];
|
||||
[copy[i], copy[j]] = [copy[j], copy[i]];
|
||||
}
|
||||
return array;
|
||||
return copy;
|
||||
}
|
||||
|
||||
export async function convertEagerImagesImportGlobToArray(
|
||||
|
||||
+5
-6
@@ -17,27 +17,26 @@ const heroImagesArray = await convertEagerImagesImportGlobToArray(heroImages);
|
||||
images={heroImagesArray}
|
||||
class="h-full w-full"
|
||||
foreground={true}
|
||||
foreground={true}
|
||||
foregroundOpacity="opacity-80"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="absolute inset-0 flex h-full w-full flex-col justify-center px-8 py-36 text-center"
|
||||
>
|
||||
<h1
|
||||
<p
|
||||
class="font-header pb-8 text-5xl font-medium text-white uppercase text-shadow-lg/75 md:text-7xl lg:text-9xl"
|
||||
>
|
||||
Oops!
|
||||
</h1>
|
||||
<h2
|
||||
</p>
|
||||
<h1
|
||||
class="font-header text-2xl font-medium text-gray-300 uppercase text-shadow-lg/75 md:text-2xl lg:text-4xl"
|
||||
>
|
||||
404 - Page not found
|
||||
</h2>
|
||||
</h1>
|
||||
|
||||
<a
|
||||
href="/"
|
||||
class="bg-primary text-md font-header hover:text-primary repeat hover:ring-primary mx-auto mt-8 inline-block rounded px-6 py-3 font-light text-white uppercase drop-shadow-lg/75 transition hover:bg-white hover:ring-2"
|
||||
class="bg-primary text-md font-header hover:text-primary hover:ring-primary mx-auto mt-8 inline-block rounded px-6 py-3 font-light text-white uppercase drop-shadow-lg/75 transition hover:bg-white hover:ring-2"
|
||||
>
|
||||
Home
|
||||
</a>
|
||||
|
||||
@@ -22,7 +22,7 @@ import { Image } from "astro:assets";
|
||||
class="mx-auto mb-8 size-125 rounded-full object-cover shadow-lg/75 transition ease-in-out md:hover:scale-150"
|
||||
height={750}
|
||||
/>
|
||||
<SectionTitle class="py-8">Dr Nathan Cummins</SectionTitle>
|
||||
<SectionTitle Tag="h1" class="py-8">Dr Nathan Cummins</SectionTitle>
|
||||
<H2>{site.tagline}</H2>
|
||||
<Paragraph
|
||||
>Nathan Cummins is a composer, orchestrator, conductor, and sound
|
||||
|
||||
@@ -11,7 +11,7 @@ import { Icon } from "astro-icon/components";
|
||||
<div class="w-full">
|
||||
<section id="contact" class="bg-primary dark:bg-gray-800">
|
||||
<div class="mx-auto max-w-4xl px-8 py-16 text-center">
|
||||
<SectionTitle class="text-white" lineColour="text-white"
|
||||
<SectionTitle Tag="h1" class="text-white" lineColour="text-white"
|
||||
>Let's get in touch!</SectionTitle
|
||||
>
|
||||
<ContactForm />
|
||||
|
||||
@@ -78,7 +78,7 @@ const tracks = (
|
||||
|
||||
<a
|
||||
href="/contact/"
|
||||
class="bg-primary text-md font-header hover:text-primary repeat hover:ring-primary mx-auto mt-8 inline-block rounded px-6 py-3 font-light text-white uppercase drop-shadow-lg/75 transition hover:bg-white hover:ring-2"
|
||||
class="bg-primary text-md font-header hover:text-primary hover:ring-primary mx-auto mt-8 inline-block rounded px-6 py-3 font-light text-white uppercase drop-shadow-lg/75 transition hover:bg-white hover:ring-2"
|
||||
>
|
||||
Get in touch!
|
||||
</a>
|
||||
@@ -98,7 +98,7 @@ const tracks = (
|
||||
<span class="block w-full text-center"
|
||||
><a
|
||||
href="/about/"
|
||||
class="bg-primary text-md font-header hover:text-primary repeat hover:ring-primary mx-auto mt-8 inline-block rounded px-6 py-3 font-light text-white uppercase drop-shadow-lg/75 transition hover:bg-white hover:ring-2"
|
||||
class="bg-primary text-md font-header hover:text-primary hover:ring-primary mx-auto mt-8 inline-block rounded px-6 py-3 font-light text-white uppercase drop-shadow-lg/75 transition hover:bg-white hover:ring-2"
|
||||
>
|
||||
Find out more
|
||||
</a></span
|
||||
@@ -121,7 +121,7 @@ const tracks = (
|
||||
><div class="flex h-full w-full items-center">
|
||||
<a
|
||||
href="/contact/"
|
||||
class="text-md font-header repeat text-primary hover:bg-primary mx-auto inline-block rounded bg-white px-6 py-3 font-light uppercase drop-shadow-lg/75 transition hover:text-white hover:ring-2 hover:ring-white"
|
||||
class="text-md font-header text-primary hover:bg-primary mx-auto inline-block rounded bg-white px-6 py-3 font-light uppercase drop-shadow-lg/75 transition hover:text-white hover:ring-2 hover:ring-white"
|
||||
>
|
||||
Get in touch!
|
||||
</a>
|
||||
@@ -150,7 +150,7 @@ const tracks = (
|
||||
</div>
|
||||
<a
|
||||
href="/projects/"
|
||||
class="bg-primary text-md font-header hover:text-primary repeat hover:ring-primary mx-auto mt-8 inline-block rounded px-6 py-3 font-light text-white uppercase drop-shadow-lg/75 transition hover:bg-white hover:ring-2"
|
||||
class="bg-primary text-md font-header hover:text-primary hover:ring-primary mx-auto mt-8 inline-block rounded px-6 py-3 font-light text-white uppercase drop-shadow-lg/75 transition hover:bg-white hover:ring-2"
|
||||
>
|
||||
Explore more projects
|
||||
</a>
|
||||
|
||||
@@ -20,7 +20,7 @@ const types = [...new Set(projects.map((project) => project.data.type))];
|
||||
<div class="w-full">
|
||||
<section id="projects" class="bg-white dark:bg-gray-950">
|
||||
<div class="mx-auto max-w-4xl px-8 py-16 text-center">
|
||||
<SectionTitle>Recent Projects</SectionTitle>
|
||||
<SectionTitle Tag="h1">Recent Projects</SectionTitle>
|
||||
<div id="filter-tokens" class="my-8 space-y-2 space-x-2">
|
||||
<Token
|
||||
class="filter-token !text-primary ring-primary bg-white ring-2 hover:cursor-pointer"
|
||||
|
||||
@@ -11,7 +11,7 @@ import MainLayout from "@layouts/MainLayout.astro";
|
||||
<div class="w-full bg-white py-16 dark:bg-gray-950">
|
||||
<section id="intro">
|
||||
<div class="mx-auto max-w-4xl px-8 text-left">
|
||||
<SectionTitle class="pb-8">Sheet Music</SectionTitle>
|
||||
<SectionTitle Tag="h1" class="pb-8">Sheet Music</SectionTitle>
|
||||
<Paragraph
|
||||
>Sheet music for many of my pieces and orchestrations is available for
|
||||
purchase or rent. If you are interested in sheet music for performance
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Fira+Code:wght@300..700&display=swap");
|
||||
@import url("https://fonts.googleapis.com/css2?family=Fira+Code:wght@300..700&display=swap");
|
||||
@import "../../public/fonts/inter/inter.css";
|
||||
@import "tailwindcss";
|
||||
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
|
||||
|
||||
Reference in new issue
Block a user