diff --git a/README.md b/README.md index ed36765..61782a3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Static Astro site with a git-based CMS (Sveltia). Code lives here; editorial content lives in the separate [`ctao/content`](https://astro-git.isl-dev.grid.cyfronet.pl/ctao/content) -repository. Four Example pages are editable; all other pages are placeholders. +repository. The Home page and four Example pages are editable; the rest are placeholders. - Live demo: https://astro.isl-dev.grid.cyfronet.pl - Editor: https://astro.isl-dev.grid.cyfronet.pl/admin/ (Gitea sign-in) @@ -42,7 +42,7 @@ Server setup is in [deploy/README.md](deploy/README.md). | Path | Purpose | |---|---| | `src/layouts/Base.astro` | Header, navigation, footer, page shell | -| `src/pages/example/[slug].astro` | Template for the editable Example pages | +| `src/layouts/ContentPage.astro` | Layout of the editable pages (Home and Examples) | | `src/data/examples.json` | Example pages: menu labels, order, slugs | | `src/data/placeholders.json` | Placeholder pages | | `src/styles/global.css` | All site styles | diff --git a/public/admin/config.yml b/public/admin/config.yml index efc1a16..093f653 100644 --- a/public/admin/config.yml +++ b/public/admin/config.yml @@ -44,6 +44,30 @@ media_libraries: optimize: true collections: + - name: home + label: "Home page" + files: + - name: home + label: "Home page" + file: "pages/home.md" + format: frontmatter + fields: + - { name: title, label: "Title", widget: string } + - { name: introduction, label: "Introduction", widget: text, required: false } + - name: images + label: "Images" + label_singular: "image" + widget: list + required: false + max: 2 + hint: "Up to two images, shown side by side." + fields: + - { name: image, label: "Image", widget: image } + - { name: alt, label: "Alternative text", widget: string, required: false, hint: "Describe the image; leave empty only for decorative images." } + - { name: caption, label: "Caption", widget: string, required: false } + - { name: body, label: "Body", widget: markdown } + - { name: buttonLabel, label: "Button label", widget: string, required: false } + - { name: buttonUrl, label: "Button destination", widget: string, required: false, hint: "A site path such as /example/lorem-ipsum/, or an https:// URL." } - name: examples label: "Example pages" files: diff --git a/public/admin/preview.css b/public/admin/preview.css index d561a40..a3127fd 100644 --- a/public/admin/preview.css +++ b/public/admin/preview.css @@ -279,6 +279,24 @@ button:hover .arrow { color: var(--text-muted); } +/* Two photos side by side, pulled up over the hero together */ +.page-figures { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1.5rem; + margin: -9rem 0 4rem; +} + +.page-figures .page-figure { + margin: 0; +} + +/* Same shape for both photos, whatever the uploads' proportions */ +.page-figures img { + aspect-ratio: 3 / 2; + object-fit: cover; +} + /* Prose ========================================================================== */ @@ -450,11 +468,18 @@ button:hover .arrow { padding-bottom: 4rem; } - .page-figure { + .page-figure, + .page-figures { margin-top: -6rem; } } +@media (max-width: 600px) { + .page-figures { + grid-template-columns: 1fr; + } +} + /* Preferences ========================================================================== */ diff --git a/scripts/content.mjs b/scripts/content.mjs index 4b082ba..37f6ce2 100644 --- a/scripts/content.mjs +++ b/scripts/content.mjs @@ -43,7 +43,7 @@ for (const { from, to } of targets) { } const pages = join(root, 'src/content/pages'); -for (const { slug } of examples) { +for (const slug of ['home', ...examples.map(({ slug }) => slug)]) { if (!existsSync(join(pages, `${slug}.md`))) { throw new Error(`Missing ${join(pages, `${slug}.md`)}. Check the content checkout, or run npm run content:link -- .`); } diff --git a/src/components/PageHero.astro b/src/components/PageHero.astro index 5b4f085..83d2a50 100644 --- a/src/components/PageHero.astro +++ b/src/components/PageHero.astro @@ -1,16 +1,16 @@ --- -interface Props { title: string; introduction?: string; example?: boolean; hasImage?: boolean } -const { title, introduction, example = false, hasImage = false } = Astro.props; +interface Props { title: string; introduction?: string; example?: boolean; breadcrumb?: boolean; hasImage?: boolean } +const { title, introduction, example = false, breadcrumb = true, hasImage = false } = Astro.props; ---
- }

{title}

{introduction &&

{introduction}

}
diff --git a/src/content.config.ts b/src/content.config.ts index fb35762..696de9b 100644 --- a/src/content.config.ts +++ b/src/content.config.ts @@ -8,17 +8,24 @@ const destination = z.string().refine( 'Use a site path, https:// URL, mailto: address, or fragment.', ); const pages = defineCollection({ - // Only these four files can become public content pages. + // Only the Home page and the four Example files can become public pages. loader: glob({ - pattern: `{${examples.map(({ slug }) => slug).join(',')}}.md`, + pattern: `{home,${examples.map(({ slug }) => slug).join(',')}}.md`, base: './src/content/pages', }), schema: z.object({ title: z.string().min(1), introduction: z.string().optional(), + // Example pages: one photo image: z.string().optional(), imageAlt: z.string().optional(), imageCaption: z.string().optional(), + // Home: up to two photos side by side + images: z.array(z.object({ + image: z.string().min(1), + alt: z.string().optional(), + caption: z.string().optional(), + })).max(2).optional(), buttonLabel: z.string().optional(), buttonUrl: destination.optional(), }), diff --git a/src/layouts/ContentPage.astro b/src/layouts/ContentPage.astro new file mode 100644 index 0000000..979b798 --- /dev/null +++ b/src/layouts/ContentPage.astro @@ -0,0 +1,36 @@ +--- +// Editable content page: hero, one photo (Example pages) or two side by side +// (Home), Markdown body and optional button. The CMS preview template in +// src/pages/admin/index.astro mirrors this markup. +import type { CollectionEntry } from 'astro:content'; +import { render } from 'astro:content'; +import Base from './Base.astro'; +import PageHero from '../components/PageHero.astro'; + +interface Props { page: CollectionEntry<'pages'>; example?: boolean; breadcrumb?: boolean } +const { page, example = false, breadcrumb = true } = Astro.props; +const { Content } = await render(page); +const { title, introduction, image, imageAlt, imageCaption, images, buttonLabel, buttonUrl } = page.data; +const photos = images ?? (image ? [{ image, alt: imageAlt, caption: imageCaption }] : []); +--- + + 0} /> + {photos.length > 0 &&
+
1 }}> + {photos.map(({ image, alt, caption }) => ( +
+ {alt + {caption &&
{caption}
} +
+ ))} +
+
} + + diff --git a/src/pages/admin/index.astro b/src/pages/admin/index.astro index 02b5fd1..9cc0cdf 100644 --- a/src/pages/admin/index.astro +++ b/src/pages/admin/index.astro @@ -36,9 +36,9 @@ const previewNames = examples.map(({ slug }) => slug); window.CMS?.registerPreviewStyle?.('/admin/preview.css'); diff --git a/src/pages/example/[slug].astro b/src/pages/example/[slug].astro index fc899c2..f0b2457 100644 --- a/src/pages/example/[slug].astro +++ b/src/pages/example/[slug].astro @@ -1,7 +1,6 @@ --- -import { getCollection, render } from 'astro:content'; -import Base from '../../layouts/Base.astro'; -import PageHero from '../../components/PageHero.astro'; +import { getCollection } from 'astro:content'; +import ContentPage from '../../layouts/ContentPage.astro'; import examples from '../../data/examples.json'; export async function getStaticPaths() { @@ -13,23 +12,5 @@ export async function getStaticPaths() { }); } const { page } = Astro.props; -const { Content } = await render(page); -const { title, introduction, image, imageAlt, imageCaption, buttonLabel, buttonUrl } = page.data; --- - - - {image &&
-
- {imageAlt - {imageCaption &&
{imageCaption}
} -
-
} - - + diff --git a/src/pages/index.astro b/src/pages/index.astro index 71f7c38..e54f49c 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,4 +1,8 @@ --- -import Placeholder from '../layouts/Placeholder.astro'; +import { getEntry } from 'astro:content'; +import ContentPage from '../layouts/ContentPage.astro'; + +const page = await getEntry('pages', 'home'); +if (!page) throw new Error('Missing Home page: pages/home.md. Check the content checkout (npm run content:link -- ).'); --- - + diff --git a/src/styles/global.css b/src/styles/global.css index b3b2251..b111d23 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -690,6 +690,24 @@ button:hover .arrow { color: var(--text-muted); } +/* Two photos side by side (Home), pulled up over the hero together */ +.page-figures { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1.5rem; + margin: -9rem 0 4rem; +} + +.page-figures .page-figure { + margin: 0; +} + +/* Same shape for both photos, whatever the uploads' proportions */ +.page-figures img { + aspect-ratio: 3 / 2; + object-fit: cover; +} + /* Prose ========================================================================== */ @@ -916,11 +934,18 @@ main { padding-bottom: 4rem; } - .page-figure { + .page-figure, + .page-figures { margin-top: -6rem; } } +@media (max-width: 600px) { + .page-figures { + grid-template-columns: 1fr; + } +} + /* Preferences ========================================================================== */