Scaffold basic page layout and site generation
Set up the basic layout of the site and greybox its content based on statically generated pages. Content pages uses general base layouts defined centrally to avoid duplicating code.
This commit is contained in:
parent
51b458103b
commit
8fb809fa95
10 changed files with 268 additions and 21 deletions
24
cli.js
24
cli.js
|
@ -1,9 +1,23 @@
|
|||
import { index } from "./build/node/index.js"
|
||||
import { pages } from "./build/node/content/pages.js"
|
||||
import { prettify, htmlDocument } from "antihtml";
|
||||
import * as fs from "node:fs"
|
||||
|
||||
if (!fs.existsSync("build/web")) {
|
||||
fs.mkdirSync("build/web");
|
||||
const outDir = "build/web";
|
||||
if (!fs.existsSync(outDir)) {
|
||||
fs.mkdirSync(outDir);
|
||||
}
|
||||
|
||||
console.log("writing build/web/index.html");
|
||||
fs.writeFileSync("build/web/index.html", index);
|
||||
for (const page of pages) {
|
||||
const dirSep = page.ref.indexOf("/");
|
||||
if (dirSep !== -1) {
|
||||
const dir = `${outDir}/${page.ref.slice(0, dirSep)}`;
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir);
|
||||
}
|
||||
}
|
||||
console.log(`writing ${outDir}/${page.ref}`);
|
||||
fs.writeFileSync(`${outDir}/${page.ref}`, htmlDocument(prettify(page.content)));
|
||||
}
|
||||
|
||||
console.log(`writing ${outDir}/style.css`);
|
||||
fs.writeFileSync(`${outDir}/style.css`, fs.readFileSync("public/style.css"));
|
||||
|
|
23
content/bases.tsx
Normal file
23
content/bases.tsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
import type { Node } from "antihtml";
|
||||
|
||||
interface BaseProps {
|
||||
title: string;
|
||||
children: Node | Node[],
|
||||
}
|
||||
export function BasePage(props: BaseProps) {
|
||||
return <html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>{props.title}</title>
|
||||
<link rel="stylesheet" href="./style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<header class="header">
|
||||
<nav>
|
||||
<a href="./index.html">Home</a> <a href="./updates.html">Updates</a> <a href="./projects.html">Projects</a>
|
||||
</nav>
|
||||
</header>
|
||||
{props.children}
|
||||
</body>
|
||||
</html>
|
||||
}
|
34
content/index.tsx
Normal file
34
content/index.tsx
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { BasePage } from "./bases.js";
|
||||
import { projects } from "./projects.js"
|
||||
import { updates } from "./updates.js"
|
||||
|
||||
const title = "Hornwitser's Site";
|
||||
export const index = {
|
||||
title,
|
||||
ref: "index.html",
|
||||
content: <BasePage title={title}>
|
||||
<main>
|
||||
<div class="hero" />
|
||||
<div class="author">
|
||||
<div style="width: 4em; height: 4em; background-color: grey" />
|
||||
<hgroup>
|
||||
<h1>Hi, I'm Hornwitser!</h1>
|
||||
<p>
|
||||
Grown up, he/him, aro, gray ace
|
||||
</p>
|
||||
</hgroup>
|
||||
</div>
|
||||
<p>
|
||||
I'm a red dragon that mostly dabble in hobby programming and the occasional artwork.
|
||||
</p>
|
||||
<h2>Latest <a href="./updates.html">Updates</a></h2>
|
||||
<ul>
|
||||
{ updates.map(update => <li><a href={"./" + update.ref}>{update.title}</a></li>)}
|
||||
</ul>
|
||||
<h2>Projects</h2>
|
||||
<ul>
|
||||
{ projects.map(project => <li><a href={"./" + project.ref}>{project.title}</a></li>)}
|
||||
</ul>
|
||||
</main>
|
||||
</BasePage>,
|
||||
};
|
12
content/pages.tsx
Normal file
12
content/pages.tsx
Normal file
|
@ -0,0 +1,12 @@
|
|||
import type { Page } from "./types.js";
|
||||
import { index } from "./index.js";
|
||||
import { updates, updatesIndex } from "./updates.js";
|
||||
import { projects, projectsIndex } from "./projects.js";
|
||||
|
||||
export const pages: Page[] = [
|
||||
index,
|
||||
updatesIndex,
|
||||
...updates,
|
||||
projectsIndex,
|
||||
...projects,
|
||||
];
|
47
content/projects.tsx
Normal file
47
content/projects.tsx
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { BasePage } from "./bases.js";
|
||||
import type { Page } from "./types.js";
|
||||
|
||||
export const projects: Page[] = [
|
||||
{
|
||||
title: "Buddhabrot renderer",
|
||||
ref: "projects/buddhabrot.html",
|
||||
},
|
||||
{
|
||||
title: "Wooden Drawing Board",
|
||||
ref: "projects/drafting-board.html",
|
||||
},
|
||||
{
|
||||
title: "Flying Hornwitser Paper Craft",
|
||||
ref: "projects/paper-hornwitser.html",
|
||||
},
|
||||
{
|
||||
title: "Prototype Soren Plush",
|
||||
ref: "projects/plush-soren.html",
|
||||
},
|
||||
{
|
||||
title: "Blender to CSS export script",
|
||||
ref: "projects/blender-css.html",
|
||||
},
|
||||
].map(page => ({
|
||||
title: page.title,
|
||||
ref: page.ref,
|
||||
content: <BasePage title={page.title}>
|
||||
<h1>{page.title}</h1>
|
||||
<p>Placeholder content</p>
|
||||
</BasePage>
|
||||
}));
|
||||
|
||||
|
||||
const title = "Hornwitser's Projects";
|
||||
export const projectsIndex: Page = {
|
||||
title,
|
||||
ref: "projects.html",
|
||||
content: <BasePage title={title}>
|
||||
<main>
|
||||
<h1>{title}</h1>
|
||||
<ul>
|
||||
{ projects.map(project => <li><a href={"./" + project.ref}>{project.title}</a></li>)}
|
||||
</ul>
|
||||
</main>
|
||||
</BasePage>
|
||||
}
|
7
content/types.ts
Normal file
7
content/types.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { Element } from "antihtml";
|
||||
|
||||
export interface Page {
|
||||
title: string,
|
||||
ref: string,
|
||||
content: Element,
|
||||
}
|
32
content/updates.tsx
Normal file
32
content/updates.tsx
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { BasePage } from "./bases.js";
|
||||
import type { Page } from "./types.js";
|
||||
|
||||
export const updates: Page[] = [
|
||||
{
|
||||
published: "2025-xx-xx",
|
||||
title: "Website Launch",
|
||||
ref: "updates/site-launch.html",
|
||||
}
|
||||
].map(page => ({
|
||||
title: page.title,
|
||||
ref: page.ref,
|
||||
content: <BasePage title={page.title}>
|
||||
<h1>{page.title}</h1>
|
||||
<p>Published: {page.published}</p>
|
||||
<p>Placeholder content</p>
|
||||
</BasePage>
|
||||
}));
|
||||
|
||||
const title = "Website Updates";
|
||||
export const updatesIndex: Page = {
|
||||
title,
|
||||
ref: "updates.html",
|
||||
content: <BasePage title={title}>
|
||||
<main>
|
||||
<h1>{title}</h1>
|
||||
<ul>
|
||||
{ updates.map(update => <li><a href={"./" + update.ref}>{update.title}</a></li>)}
|
||||
</ul>
|
||||
</main>
|
||||
</BasePage>
|
||||
}
|
15
index.tsx
15
index.tsx
|
@ -1,15 +0,0 @@
|
|||
import { htmlDocument, prettify } from "antihtml";
|
||||
|
||||
export const index = htmlDocument(
|
||||
prettify(
|
||||
<html>
|
||||
<head>
|
||||
<title>My Website</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>My Website</h1>
|
||||
<p>Hello world!</p>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
);
|
92
public/style.css
Normal file
92
public/style.css
Normal file
|
@ -0,0 +1,92 @@
|
|||
/* CSS Reset based on https://piccalil.li/blog/a-more-modern-css-reset/ */
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
/* Kill all default margins and paddings */
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
-moz-text-size-adjust: none;
|
||||
-webkit-text-size-adjust: none;
|
||||
text-size-adjust: none;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, button, input, label {
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
img, svg, picture {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
textarea:not([rows]) {
|
||||
min-height: 10em;
|
||||
}
|
||||
|
||||
:target {
|
||||
scroll-margin-block: 2.5em;
|
||||
}
|
||||
|
||||
/* Overall styling */
|
||||
html {
|
||||
color-scheme: light dark;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
hgroup h1 {
|
||||
margin-bottom: 0.1em;
|
||||
}
|
||||
hgroup p {
|
||||
font-style: italic;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
margin-block-start: 1.25em;
|
||||
margin-block-end: 0.5em;
|
||||
}
|
||||
|
||||
ol, ul {
|
||||
padding-inline-start: 1.25em;
|
||||
}
|
||||
|
||||
/* Base Page Layout */
|
||||
body {
|
||||
max-width: 50rem;
|
||||
padding: 0;
|
||||
margin-block: 0;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding-block: 1em;
|
||||
}
|
||||
|
||||
.hero {
|
||||
height: 30em;
|
||||
background-color: grey;
|
||||
}
|
||||
|
||||
/* index */
|
||||
.author {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
align-items: center;
|
||||
gap: 1em;
|
||||
margin-block: 1em;
|
||||
}
|
||||
.author h1 {
|
||||
margin-block-start: 0;
|
||||
}
|
||||
.author p {
|
||||
margin-block-end: 0;
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"files": ["index.tsx"],
|
||||
"include": ["content"],
|
||||
"compilerOptions": {
|
||||
"outDir": "build/node",
|
||||
"rootDir": ".",
|
||||
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "antihtml",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue