paperastro 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ {
2
+ "recommendations": ["astro-build.astro-vscode", "unifiedjs.vscode-mdx"],
3
+ "unwantedRecommendations": []
4
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "command": "./node_modules/.bin/astro dev",
6
+ "name": "Development server",
7
+ "request": "launch",
8
+ "type": "node-terminal"
9
+ }
10
+ ]
11
+ }
package/README.md ADDED
@@ -0,0 +1,125 @@
1
+
2
+ # PaperAstro
3
+
4
+ PaperAstro is a lightweight, professional Astro starter template styled with PaperCSS.
5
+ It provides a clean, sketch‑inspired aesthetic while maintaining strong performance, clarity, and simplicity.
6
+
7
+ This template includes built‑in support for form handling through the Fabform backend.
8
+ Learn more at: [https://fabform.io](https://fabform.io)
9
+
10
+ ---
11
+
12
+ ## Installation
13
+
14
+ ### Install with npm (recommended)
15
+
16
+ ```sh
17
+ npm create astro@latest -- --template @fabform/paperastro
18
+ ```
19
+
20
+ This scaffolds a fresh project using the latest version of the PaperAstro starter.
21
+ You can explore npm installation.
22
+
23
+ ### Install with Git
24
+
25
+ ```sh
26
+ git clone https://github.com/fabformhub/paperastro
27
+ cd paperastro
28
+ npm install
29
+ npm run dev
30
+ ```
31
+
32
+ You can explore Git installation.
33
+
34
+ ---
35
+
36
+ ## Screenshot
37
+
38
+ ![PaperAstro Screenshot](public/screenshot.png)
39
+
40
+
41
+ ---
42
+
43
+ ## Demo
44
+
45
+ Live Demo: [https://paperastro.vercel.app/](https://paperastro.vercel.app/)
46
+
47
+ ---
48
+
49
+ ## Features
50
+
51
+ - Built with Astro 5
52
+ - Styled entirely with PaperCSS
53
+ - MDX support included
54
+ - RSS and Sitemap integrations
55
+ - Sharp for image optimization
56
+ - Native form handling powered by Fabform
57
+ - Minimal, well‑structured project layout
58
+ - Suitable for blogs, documentation, and lightweight content sites
59
+
60
+ ---
61
+
62
+ ## Project Structure
63
+
64
+ ```
65
+ /
66
+ ├── public/
67
+ ├── src/
68
+ │ ├── components/
69
+ │ ├── layouts/
70
+ │ ├── pages/
71
+ │ ├── consts.ts
72
+ │ └── styles/
73
+ └── package.json
74
+ ```
75
+
76
+ You can explore project structure.
77
+
78
+ ---
79
+
80
+ ## Commands
81
+
82
+ ```sh
83
+ npm run dev # Start the development server
84
+ npm run build # Build the site for production
85
+ npm run preview # Preview the production build
86
+ ```
87
+
88
+ You can explore Astro commands.
89
+
90
+ ---
91
+
92
+ ## Form Handling with Fabform
93
+
94
+ PaperAstro includes ready‑to‑use form integration powered by the Fabform backend.
95
+ You can connect any form by adding your Fabform endpoint URL.
96
+
97
+ Learn more at: [https://fabform.io](https://fabform.io)
98
+
99
+ ---
100
+
101
+ ## Deploy with Vercel
102
+
103
+ Deploy PaperAstro to Vercel with one click:
104
+
105
+ [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/fabformhub/paperastro)
106
+
107
+ ---
108
+
109
+ ## License
110
+
111
+ MIT License
112
+ © 2026 Geoffrey Callaghan
113
+
114
+ ---
115
+
116
+ ## Repository
117
+
118
+ GitHub: [https://github.com/fabformhub/paperastro](https://github.com/fabformhub/paperastro)
119
+
120
+ ## npm Package
121
+
122
+ [https://www.npmjs.com/package/@fabform/paperastro](https://www.npmjs.com/package/@fabform/paperastro)
123
+ You can explore the npm package.
124
+
125
+ ---
@@ -0,0 +1,11 @@
1
+ // @ts-check
2
+
3
+ import mdx from '@astrojs/mdx';
4
+ import sitemap from '@astrojs/sitemap';
5
+ import { defineConfig } from 'astro/config';
6
+
7
+ // https://astro.build/config
8
+ export default defineConfig({
9
+ site: 'https://example.com',
10
+ integrations: [mdx(), sitemap()],
11
+ });
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "paperastro",
3
+ "version": "1.0.3",
4
+ "type": "module",
5
+ "description": "A lightweight, professional Astro starter template styled with PaperCSS and powered by Fabform.",
6
+ "author": "Geoffrey Callaghan",
7
+ "license": "MIT",
8
+ "homepage": "https://github.com/fabformhub/paperastro#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/fabformhub/paperastro.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/fabformhub/paperastro/issues"
15
+ },
16
+ "keywords": [
17
+ "astro",
18
+ "starter",
19
+ "template",
20
+ "papercss",
21
+ "blog",
22
+ "documentation",
23
+ "fabform",
24
+ "static-site",
25
+ "astro-theme"
26
+ ],
27
+ "scripts": {
28
+ "dev": "astro dev",
29
+ "build": "astro build",
30
+ "preview": "astro preview",
31
+ "astro": "astro"
32
+ },
33
+ "dependencies": {
34
+ "@astrojs/mdx": "^4.3.13",
35
+ "@astrojs/rss": "^4.0.15",
36
+ "@astrojs/sitemap": "^3.7.0",
37
+ "astro": "^5.17.1",
38
+ "sharp": "^0.34.3"
39
+ }
40
+ }
41
+
Binary file
@@ -0,0 +1,9 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
2
+ <path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
3
+ <style>
4
+ path { fill: #000; }
5
+ @media (prefers-color-scheme: dark) {
6
+ path { fill: #FFF; }
7
+ }
8
+ </style>
9
+ </svg>
Binary file
Binary file
Binary file
@@ -0,0 +1,63 @@
1
+ ---
2
+ // Import the global.css file here so that it is included on
3
+ // all pages through the use of the <BaseHead /> component.
4
+ import '../styles/global.css';
5
+ import type { ImageMetadata } from 'astro';
6
+ import FallbackImage from '../assets/scrap-placeholder.png';
7
+ import { SITE_TITLE } from '../consts';
8
+
9
+ interface Props {
10
+ title: string;
11
+ description: string;
12
+ image?: ImageMetadata;
13
+ }
14
+
15
+ const canonicalURL = new URL(Astro.url.pathname, Astro.site);
16
+
17
+ const { title, description, image = FallbackImage } = Astro.props;
18
+ ---
19
+
20
+ <!-- Global Metadata -->
21
+ <meta charset="utf-8" />
22
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
23
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
24
+ <link rel="icon" href="/favicon.ico" />
25
+ <link rel="sitemap" href="/sitemap-index.xml" />
26
+
27
+ <!-- Global CSS -->
28
+ <link rel="stylesheet" href="https://unpkg.com/papercss@1.9.2/dist/paper.min.css" />
29
+
30
+ <link
31
+ rel="alternate"
32
+ type="application/rss+xml"
33
+ title={SITE_TITLE}
34
+ href={new URL('rss.xml', Astro.site)}
35
+ />
36
+ <meta name="generator" content={Astro.generator} />
37
+
38
+ <!-- Font preloads -->
39
+ <link rel="preload" href="/fonts/atkinson-regular.woff" as="font" type="font/woff" crossorigin />
40
+ <link rel="preload" href="/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin />
41
+
42
+ <!-- Canonical URL -->
43
+ <link rel="canonical" href={canonicalURL} />
44
+
45
+ <!-- Primary Meta Tags -->
46
+ <title>{title}</title>
47
+ <meta name="title" content={title} />
48
+ <meta name="description" content={description} />
49
+
50
+ <!-- Open Graph / Facebook -->
51
+ <meta property="og:type" content="website" />
52
+ <meta property="og:url" content={Astro.url} />
53
+ <meta property="og:title" content={title} />
54
+ <meta property="og:description" content={description} />
55
+ <meta property="og:image" content={new URL(image.src, Astro.url)} />
56
+
57
+ <!-- Twitter -->
58
+ <meta property="twitter:card" content="summary_large_image" />
59
+ <meta property="twitter:url" content={Astro.url} />
60
+ <meta property="twitter:title" content={title} />
61
+ <meta property="twitter:description" content={description} />
62
+ <meta property="twitter:image" content={new URL(image.src, Astro.url)} />
63
+
@@ -0,0 +1,34 @@
1
+ ---
2
+ const year = new Date().getFullYear();
3
+ ---
4
+
5
+ <footer class="paper padding-large text-center margin-top-large">
6
+
7
+ <p class="paper">
8
+ © {year} PaperAstro. Built with Astro and styled using PaperCSS. Forms powered by the
9
+ <a href="https://fabform.io" target="_blank">form backend service</a> fabform.io.
10
+ </p>
11
+
12
+ <div class="social-links flex justify-center gap-small margin-top-large">
13
+
14
+ <!-- GitHub -->
15
+ <a href="https://github.com/fabformhub/paperastro" target="_blank" class="paper-btn btn-small flex align-center gap-small">
16
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
17
+ <path d="M12 2a10 10 0 0 0-3 19.5c.5.1.7-.2.7-.5v-2c-3 .7-3.6-1.5-3.6-1.5-.5-1.2-1.2-1.5-1.2-1.5-1-.7.1-.7.1-.7 1 .1 1.6 1 1.6 1 .9 1.6 2.4 1.1 3 .8.1-.7.4-1.1.7-1.4-2.4-.3-5-1.2-5-5.4 0-1.1.4-2 1-2.7-.1-.3-.4-1.3.1-2.6 0 0 .8-.3 2.7 1a9.3 9.3 0 0 1 5 0c1.9-1.3 2.7-1 2.7-1 .5 1.3.2 2.3.1 2.6.6.7 1 1.6 1 2.7 0 4.2-2.6 5.1-5 5.4.4.3.8 1 .8 2v3c0 .3.2.6.7.5A10 10 0 0 0 12 2z"/>
18
+ </svg>
19
+ GitHub
20
+ </a>
21
+
22
+ </div>
23
+
24
+ </footer>
25
+
26
+ <style>
27
+ .gap-small {
28
+ gap: 0.5rem;
29
+ }
30
+ svg {
31
+ display: inline-block;
32
+ }
33
+ </style>
34
+
@@ -0,0 +1,17 @@
1
+ ---
2
+ interface Props {
3
+ date: Date;
4
+ }
5
+
6
+ const { date } = Astro.props;
7
+ ---
8
+
9
+ <time datetime={date.toISOString()}>
10
+ {
11
+ date.toLocaleDateString('en-us', {
12
+ year: 'numeric',
13
+ month: 'short',
14
+ day: 'numeric',
15
+ })
16
+ }
17
+ </time>
@@ -0,0 +1,21 @@
1
+ ---
2
+ import { SITE_TITLE } from '../consts';
3
+ import HeaderLink from './HeaderLink.astro';
4
+ ---
5
+
6
+ <header class="paper">
7
+ <div class="container">
8
+ <nav class="padding-small flex align-center space-between">
9
+
10
+ <!-- Navigation Links -->
11
+ <div class="flex gap-small">
12
+ <HeaderLink href="/" class="paper-btn btn-small">{SITE_TITLE}</HeaderLink>
13
+ <HeaderLink href="/blog" class="paper-btn btn-small">Blog</HeaderLink>
14
+ <HeaderLink href="/about" class="paper-btn btn-small">About</HeaderLink>
15
+ <HeaderLink href="/contact" class="paper-btn btn-small">Contact</HeaderLink>
16
+ </div>
17
+
18
+ </nav>
19
+ </div>
20
+ </header>
21
+
@@ -0,0 +1,45 @@
1
+ ---
2
+ import type { HTMLAttributes } from 'astro/types';
3
+
4
+ type Props = HTMLAttributes<'a'>;
5
+
6
+ const { href, class: className, ...props } = Astro.props;
7
+ const pathname = Astro.url.pathname.replace(import.meta.env.BASE_URL, '');
8
+ const subpath = pathname.match(/[^\/]+/g);
9
+ const isActive = href === pathname || href === '/' + (subpath?.[0] || '');
10
+ ---
11
+
12
+ <a
13
+ href={href}
14
+ class:list={[
15
+ "paper-btn btn-small no-decoration",
16
+ className,
17
+ { active: isActive }
18
+ ]}
19
+ {...props}
20
+ >
21
+ <slot />
22
+ </a>
23
+
24
+ <style>
25
+ a {
26
+ display: inline-block;
27
+ }
28
+
29
+ .no-decoration {
30
+ text-decoration: none;
31
+ }
32
+
33
+ /* Active state */
34
+ a.active {
35
+ font-weight: bold;
36
+ box-shadow: 2px 2px 0 #000;
37
+ transform: translate(-1px, -1px);
38
+ transition: transform 0.05s ease;
39
+ }
40
+
41
+ a.active:hover {
42
+ transform: translate(-2px, -2px);
43
+ }
44
+ </style>
45
+
package/src/consts.ts ADDED
@@ -0,0 +1,6 @@
1
+ // Place any global data in this file.
2
+ // You can import this data from anywhere in your site by using the `import` keyword.
3
+
4
+ export const SITE_TITLE = 'PaperAstro';
5
+ export const SITE_DESCRIPTION = 'A hand‑drawn, lightweight Astro starter built with PaperCSS.';
6
+
@@ -0,0 +1,39 @@
1
+ ---
2
+ title: "Getting Started with PaperAstro"
3
+ description: "An introduction to the PaperAstro starter template and how to begin using it."
4
+ pubDate: "Jan 1 2026"
5
+ heroImage: "../../assets/scrap-placeholder.png"
6
+ ---
7
+
8
+ PaperAstro is a lightweight starter template designed to help developers build clean, content‑focused websites using Astro and PaperCSS. This example post demonstrates how a typical article is structured within the template.
9
+
10
+ ## Why PaperAstro exists
11
+
12
+ Many developers want a simple, minimal foundation for blogs or documentation sites without the overhead of a full design system. PaperAstro provides:
13
+
14
+ - A sketch‑inspired aesthetic powered by PaperCSS
15
+ - A clear and minimal project structure
16
+ - Built‑in support for MDX, RSS, and sitemaps
17
+ - Form handling through Fabform.io
18
+ - Fast performance enabled by Astro’s modern architecture
19
+
20
+ ## Writing content
21
+
22
+ Blog posts are stored in the `src/content/blog/` directory and use frontmatter to define metadata such as:
23
+
24
+ - `title`
25
+ - `description`
26
+ - `pubDate`
27
+ - `heroImage`
28
+
29
+ Content can be written in Markdown or MDX, depending on your needs.
30
+
31
+ ## Adding images
32
+
33
+ Hero images can be added by referencing files inside the `src/assets/` directory. PaperAstro uses Astro’s built‑in image optimization to ensure images are responsive and efficient.
34
+
35
+ ## Customizing the layout
36
+
37
+ You can modify the blog layout by editing:
38
+
39
+
@@ -0,0 +1,72 @@
1
+ ---
2
+ title: "Understanding the PaperAstro Project Structure"
3
+ description: "A walkthrough of the core folders and files that make up the PaperAstro starter template."
4
+ pubDate: "Jan 11 2026"
5
+ heroImage: "../../assets/scrap-placeholder.png"
6
+ ---
7
+
8
+ PaperAstro is designed to be minimal, clear, and easy to extend. This post provides an overview of the project structure so you can quickly understand how the template is organized and where to make changes.
9
+
10
+ ## Key directories
11
+
12
+ The main folders you will work with include:
13
+
14
+ ### `src/pages/`
15
+ This directory contains your site’s routes. Each `.astro` or `.md` file becomes a page on your website.
16
+ Examples include:
17
+
18
+ - `index.astro` for the homepage
19
+ - `about.astro` for the About page
20
+ - `contact.astro` for the contact form
21
+
22
+ ### `src/components/`
23
+ Reusable UI components such as the header, footer, and utility components live here.
24
+ You can add your own components to keep your layout clean and modular.
25
+
26
+ ### `src/layouts/`
27
+ Layouts define the structure shared across multiple pages.
28
+ For example, `BlogPost.astro` controls how individual blog posts are displayed.
29
+
30
+ ### `src/content/blog/`
31
+ This folder contains your Markdown or MDX blog posts.
32
+ Each post includes frontmatter metadata such as:
33
+
34
+ - `title`
35
+ - `description`
36
+ - `pubDate`
37
+ - `heroImage`
38
+
39
+ ## Assets and styling
40
+
41
+ ### `src/assets/`
42
+ Images, hero graphics, and other static assets are stored here.
43
+ Astro’s image optimization pipeline ensures they are responsive and efficient.
44
+
45
+ ### PaperCSS styling
46
+ PaperAstro uses PaperCSS for all styling. No custom CSS is required, but you can extend the design by adding your own styles if needed.
47
+
48
+ ## Configuration files
49
+
50
+ ### `astro.config.mjs`
51
+ Controls Astro integrations, build settings, and site configuration.
52
+
53
+ ### `src/consts.ts`
54
+ Stores global constants such as the site title and description.
55
+
56
+ ## Extending the template
57
+
58
+ PaperAstro is intentionally minimal, giving you the freedom to:
59
+
60
+ - Add new pages
61
+ - Create custom layouts
62
+ - Integrate additional Astro features
63
+ - Expand the blog section
64
+ - Introduce new components
65
+
66
+ The structure is designed to stay out of your way while providing a solid foundation for content‑driven websites.
67
+
68
+ ## Summary
69
+
70
+ Understanding the project structure is the first step toward customizing PaperAstro for your needs.
71
+ With a clear layout and minimal overhead, you can quickly build a site that is both functional and visually distinctive.
72
+
@@ -0,0 +1,19 @@
1
+ import { defineCollection, z } from 'astro:content';
2
+ import { glob } from 'astro/loaders';
3
+
4
+ const blog = defineCollection({
5
+ // Load Markdown and MDX files in the `src/content/blog/` directory.
6
+ loader: glob({ base: './src/content/blog', pattern: '**/*.{md,mdx}' }),
7
+ // Type-check frontmatter using a schema
8
+ schema: ({ image }) =>
9
+ z.object({
10
+ title: z.string(),
11
+ description: z.string(),
12
+ // Transform string to Date object
13
+ pubDate: z.coerce.date(),
14
+ updatedDate: z.coerce.date().optional(),
15
+ heroImage: image().optional(),
16
+ }),
17
+ });
18
+
19
+ export const collections = { blog };
@@ -0,0 +1,73 @@
1
+ ---
2
+ import { Image } from 'astro:assets';
3
+ import type { CollectionEntry } from 'astro:content';
4
+ import BaseHead from '../components/BaseHead.astro';
5
+ import Footer from '../components/Footer.astro';
6
+ import FormattedDate from '../components/FormattedDate.astro';
7
+ import Header from '../components/Header.astro';
8
+
9
+ type Props = CollectionEntry<'blog'>['data'];
10
+
11
+ const { title, description, pubDate, updatedDate, heroImage } = Astro.props;
12
+ ---
13
+
14
+ <html lang="en">
15
+ <head>
16
+ <BaseHead title={title} description={description} />
17
+ </head>
18
+
19
+ <body>
20
+ <Header />
21
+
22
+ <main class="container padding-large">
23
+ <article class="paper">
24
+
25
+ <!-- Hero Image -->
26
+ <div class="hero-image margin-bottom-large">
27
+ {heroImage && (
28
+ <Image
29
+ width={1020}
30
+ height={510}
31
+ src={heroImage}
32
+ alt={title}
33
+ class="img-responsive paper"
34
+ />
35
+ )}
36
+ </div>
37
+
38
+ <!-- Title + Dates -->
39
+ <div class="text-center margin-bottom-large">
40
+ <h1 class="paper">{title}</h1>
41
+
42
+ <p class="paper">
43
+ Published on <FormattedDate date={pubDate} />
44
+ {updatedDate && (
45
+ <>
46
+ <br />
47
+ <span class="italic">
48
+ Last updated on <FormattedDate date={updatedDate} />
49
+ </span>
50
+ </>
51
+ )}
52
+ </p>
53
+
54
+ <hr />
55
+ </div>
56
+
57
+ <!-- Blog Content -->
58
+ <div class="paper padding-large">
59
+ <slot />
60
+ </div>
61
+
62
+ <!-- Footer Note -->
63
+ <p class="text-center margin-top-large paper">
64
+ Thank you for reading. This article is part of the PaperAstro starter template.
65
+ </p>
66
+
67
+ </article>
68
+ </main>
69
+
70
+ <Footer />
71
+ </body>
72
+ </html>
73
+
@@ -0,0 +1,47 @@
1
+ ---
2
+ import AboutHeroImage from '../assets/scrap-placeholder.png';
3
+ import Layout from '../layouts/BlogPost.astro';
4
+ ---
5
+
6
+ <Layout
7
+ title="About PaperAstro"
8
+ description="Learn more about the purpose and philosophy behind the PaperAstro starter template."
9
+ pubDate={new Date('August 08 2021')}
10
+ heroImage={AboutHeroImage}
11
+ >
12
+ <p class="paper">
13
+ PaperAstro is a lightweight Astro starter template designed for developers who want a clean,
14
+ sketch‑inspired aesthetic without sacrificing performance or simplicity. It combines the speed
15
+ and flexibility of Astro with the hand‑drawn visual style of PaperCSS, resulting in a unique
16
+ foundation for blogs, documentation sites, and small web projects.
17
+ </p>
18
+
19
+ <p class="paper">
20
+ This project was created to offer a minimal, approachable starting point for developers who
21
+ appreciate clarity and structure. PaperAstro avoids unnecessary complexity and focuses on
22
+ providing a solid, well‑organized base that can be extended in any direction.
23
+ </p>
24
+
25
+ <p class="paper">
26
+ Key principles behind PaperAstro include:
27
+ </p>
28
+
29
+ <ul class="paper">
30
+ <li>Clean, readable code that is easy to understand and modify</li>
31
+ <li>A minimal design system powered entirely by PaperCSS</li>
32
+ <li>Fast performance enabled by Astro’s modern architecture</li>
33
+ <li>Practical defaults for content‑driven websites</li>
34
+ </ul>
35
+
36
+ <p class="paper">
37
+ PaperAstro also includes built‑in support for form handling through Fabform, making it simple
38
+ to add contact forms or other user input without managing backend infrastructure.
39
+ </p>
40
+
41
+ <p class="paper">
42
+ Whether you're building a personal site, a small publication, or a documentation hub,
43
+ PaperAstro provides a reliable foundation that stays out of your way while giving your project
44
+ a distinctive visual character.
45
+ </p>
46
+ </Layout>
47
+
@@ -0,0 +1,20 @@
1
+ ---
2
+ import { type CollectionEntry, getCollection, render } from 'astro:content';
3
+ import BlogPost from '../../layouts/BlogPost.astro';
4
+
5
+ export async function getStaticPaths() {
6
+ const posts = await getCollection('blog');
7
+ return posts.map((post) => ({
8
+ params: { slug: post.id },
9
+ props: post,
10
+ }));
11
+ }
12
+ type Props = CollectionEntry<'blog'>;
13
+
14
+ const post = Astro.props;
15
+ const { Content } = await render(post);
16
+ ---
17
+
18
+ <BlogPost {...post.data}>
19
+ <Content />
20
+ </BlogPost>
@@ -0,0 +1,87 @@
1
+ ---
2
+ import { Image } from 'astro:assets';
3
+ import { getCollection } from 'astro:content';
4
+ import BaseHead from '../../components/BaseHead.astro';
5
+ import Footer from '../../components/Footer.astro';
6
+ import FormattedDate from '../../components/FormattedDate.astro';
7
+ import Header from '../../components/Header.astro';
8
+ import { SITE_DESCRIPTION, SITE_TITLE } from '../../consts';
9
+
10
+ const posts = (await getCollection('blog')).sort(
11
+ (a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
12
+ );
13
+ ---
14
+
15
+ <!doctype html>
16
+ <html lang="en">
17
+ <head>
18
+ <BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
19
+ </head>
20
+
21
+ <body>
22
+ <Header />
23
+
24
+ <main class="container padding-large">
25
+
26
+ <h1 class="paper text-center">
27
+ <span class="inline-icon">
28
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
29
+ <rect x="3" y="4" width="18" height="16" rx="2" ry="2" />
30
+ <line x1="7" y1="8" x2="17" y2="8" />
31
+ <line x1="7" y1="12" x2="17" y2="12" />
32
+ <line x1="7" y1="16" x2="13" y2="16" />
33
+ </svg>
34
+ </span>
35
+ The Papery Blog Roll
36
+ </h1>
37
+
38
+ <p class="paper text-center">
39
+ Welcome to the official archive of all things scribbled, doodled, typed,
40
+ or accidentally published. Each post below has been lovingly handcrafted
41
+ using Astro and PaperCSS, then pinned to this digital bulletin board for
42
+ your reading pleasure.
43
+ </p>
44
+
45
+ <section class="margin-top-large">
46
+ <ul class="flex flex-wrap gap-large no-bullet justify-center">
47
+ {
48
+ posts.map((post) => (
49
+ <li class="paper padding-small" style="width: 100%; max-width: 420px;">
50
+ <a href={`/blog/${post.id}/`} class="no-decoration">
51
+ {post.data.heroImage && (
52
+ <Image
53
+ width={720}
54
+ height={360}
55
+ src={post.data.heroImage}
56
+ alt=""
57
+ class="img-responsive paper margin-bottom-small"
58
+ />
59
+ )}
60
+
61
+ <h4 class="margin-none">{post.data.title}</h4>
62
+
63
+ <p class="italic margin-none">
64
+ <FormattedDate date={post.data.pubDate} />
65
+ </p>
66
+ </a>
67
+ </li>
68
+ ))
69
+ }
70
+ </ul>
71
+ </section>
72
+
73
+ <p class="paper text-center margin-top-large">
74
+ <span class="inline-icon">
75
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
76
+ <path d="M5 19h14M5 5h14M5 12h8" />
77
+ </svg>
78
+ </span>
79
+ That’s all for now. More papery posts are already scribbled in the margins.
80
+ </p>
81
+
82
+ </main>
83
+
84
+ <Footer />
85
+ </body>
86
+ </html>
87
+
@@ -0,0 +1,114 @@
1
+ ---
2
+ import BaseHead from '../components/BaseHead.astro';
3
+ import Header from '../components/Header.astro';
4
+ import Footer from '../components/Footer.astro';
5
+ import { SITE_TITLE, SITE_DESCRIPTION } from '../consts';
6
+ ---
7
+
8
+ <!doctype html>
9
+ <html lang="en">
10
+ <head>
11
+ <BaseHead title={`${SITE_TITLE} – Contact`} description={SITE_DESCRIPTION} />
12
+ </head>
13
+
14
+ <body>
15
+ <Header />
16
+
17
+ <!--
18
+ ============================
19
+ FABFORM.IO INTEGRATION NOTES
20
+ ============================
21
+
22
+ Replace YOUR-ENDPOINT-ID-HERE with your Fabform endpoint.
23
+ Example: https://fabform.io/f/abc123
24
+
25
+ Do NOT remove name="" attributes — Fabform needs them.
26
+ Do NOT expose API keys — Fabform does not require any.
27
+ -->
28
+
29
+ <main class="container padding-large">
30
+
31
+ <div class="paper padding-large">
32
+
33
+ <h1 class="text-center">
34
+ <span class="inline-icon">
35
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
36
+ <rect x="3" y="4" width="18" height="16" rx="2" ry="2" />
37
+ <line x1="7" y1="8" x2="17" y2="8" />
38
+ </svg>
39
+ </span>
40
+ Contact
41
+ </h1>
42
+
43
+ <p class="text-center">
44
+ Use the form below to send a message.
45
+ This form is styled with PaperCSS and securely processed by Fabform.io.
46
+ </p>
47
+
48
+ <form
49
+ action="https://fabform.io/f/YOUR-ENDPOINT-ID-HERE"
50
+ method="POST"
51
+ class="margin-top-large"
52
+ >
53
+
54
+ <div class="form-group">
55
+ <label for="name">Your Name</label>
56
+ <input class="input-block" type="text" id="name" name="name" required>
57
+ </div>
58
+
59
+ <div class="form-group">
60
+ <label for="email">Your Email</label>
61
+ <input class="input-block" type="email" id="email" name="email" required>
62
+ </div>
63
+
64
+ <div class="form-group">
65
+ <label for="subject">Subject</label>
66
+ <select id="subject" name="subject" class="input-block">
67
+ <option value="general">General Message</option>
68
+ <option value="feedback">Feedback</option>
69
+ <option value="support">Support Request</option>
70
+ </select>
71
+ </div>
72
+
73
+ <div class="form-group">
74
+ <label for="message">Your Message</label>
75
+ <textarea class="input-block no-resize" id="message" name="message" rows="6" required></textarea>
76
+ </div>
77
+
78
+ <fieldset class="form-group">
79
+ <legend>Stay Updated</legend>
80
+
81
+ <label class="paper-check" for="newsletter">
82
+ <input type="checkbox" id="newsletter" name="newsletter" value="yes">
83
+ <span>Subscribe to updates</span>
84
+ </label>
85
+ </fieldset>
86
+
87
+ <button type="submit" class="paper-btn btn-primary btn-block margin-top">
88
+ Send Message
89
+ </button>
90
+
91
+ </form>
92
+
93
+ <div class="text-center margin-top-large">
94
+ <a href="https://fabform.io" target="_blank" class="badge secondary">
95
+ Powered by Fabform.io
96
+ </a>
97
+ </div>
98
+
99
+ </div>
100
+
101
+ </main>
102
+
103
+ <Footer />
104
+
105
+ <style>
106
+ .inline-icon {
107
+ display: inline-flex;
108
+ vertical-align: middle;
109
+ margin-right: 0.35rem;
110
+ }
111
+ </style>
112
+ </body>
113
+ </html>
114
+
@@ -0,0 +1,75 @@
1
+ ---
2
+ import BaseHead from '../components/BaseHead.astro';
3
+ import Footer from '../components/Footer.astro';
4
+ import Header from '../components/Header.astro';
5
+ import { SITE_DESCRIPTION, SITE_TITLE } from '../consts';
6
+ ---
7
+
8
+ <!doctype html>
9
+ <html lang="en">
10
+ <head>
11
+ <BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
12
+ </head>
13
+ <body>
14
+ <Header />
15
+
16
+ <main class="container padding-large">
17
+
18
+ <h1 class="text-center paper">Welcome to PaperAstro</h1>
19
+
20
+ <p class="paper">
21
+ PaperAstro is a lightweight, professional starter template built with Astro and styled
22
+ entirely using PaperCSS. It provides a clean, sketch‑inspired aesthetic while maintaining
23
+ strong performance, clarity, and simplicity.
24
+ </p>
25
+
26
+ <p class="paper">
27
+ This template is designed for developers who want a minimal, well‑structured foundation
28
+ for blogs, documentation sites, or small content‑driven projects. PaperAstro keeps the
29
+ design approachable and distinctive without adding unnecessary complexity.
30
+ </p>
31
+
32
+ <h2 class="paper">Features</h2>
33
+
34
+ <ul class="paper">
35
+ <li>Clean, minimal layout powered by Astro</li>
36
+ <li>Sketch‑style UI using PaperCSS</li>
37
+ <li>MDX support for rich content</li>
38
+ <li>RSS and Sitemap integrations included</li>
39
+ <li>Image optimization via Sharp</li>
40
+ <li>Built‑in form handling through Fabform.io</li>
41
+ </ul>
42
+
43
+ <h2 class="paper">Getting Started</h2>
44
+
45
+ <p class="paper">
46
+ To begin customizing this template, edit the files inside the <code>src</code> directory.
47
+ The structure is intentionally simple, making it easy to adapt the layout, add pages,
48
+ or integrate additional Astro features.
49
+ </p>
50
+
51
+ <ul class="paper">
52
+ <li>Modify this page at <code>src/pages/index.astro</code></li>
53
+ <li>Add new routes inside <code>src/pages/</code></li>
54
+ <li>Update global settings in <code>src/consts.ts</code></li>
55
+ <li>Extend layouts or components as needed</li>
56
+ </ul>
57
+
58
+ <h2 class="paper">Resources</h2>
59
+
60
+ <ul class="paper">
61
+ <li><a href="https://docs.astro.build/">Astro Documentation</a></li>
62
+ <li><a href="https://www.getpapercss.com/">PaperCSS Documentation</a></li>
63
+ <li><a href="https://fabform.io">Fabform.io</a> for form handling</li>
64
+ </ul>
65
+
66
+ <p class="paper text-center">
67
+ PaperAstro provides a focused, reliable starting point for your next project.
68
+ </p>
69
+
70
+ </main>
71
+
72
+ <Footer />
73
+ </body>
74
+ </html>
75
+
@@ -0,0 +1,16 @@
1
+ import { getCollection } from 'astro:content';
2
+ import rss from '@astrojs/rss';
3
+ import { SITE_DESCRIPTION, SITE_TITLE } from '../consts';
4
+
5
+ export async function GET(context) {
6
+ const posts = await getCollection('blog');
7
+ return rss({
8
+ title: SITE_TITLE,
9
+ description: SITE_DESCRIPTION,
10
+ site: context.site,
11
+ items: posts.map((post) => ({
12
+ ...post.data,
13
+ link: `/blog/${post.id}/`,
14
+ })),
15
+ });
16
+ }
Binary file
File without changes
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "astro/tsconfigs/strict",
3
+ "include": [".astro/types.d.ts", "**/*"],
4
+ "exclude": ["dist"],
5
+ "compilerOptions": {
6
+ "strictNullChecks": true
7
+ }
8
+ }