Compare commits
10 Commits
71af009bbf
...
97526cf71f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97526cf71f | ||
|
|
6dd7e8874e | ||
|
|
06c6523b5d | ||
|
|
77797231d3 | ||
|
|
29b398921b | ||
|
|
1a9b58d7d7 | ||
|
|
9957a5c116 | ||
|
|
3fe75e9ad5 | ||
|
|
463250eb7b | ||
|
|
472aad6bc6 |
34
.air.toml
Normal file
34
.air.toml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
root = "."
|
||||||
|
|
||||||
|
[build]
|
||||||
|
cmd = "go run main.go"
|
||||||
|
bin = ""
|
||||||
|
delay = 1000
|
||||||
|
exclude_dir = ["assets", "tmp", "vendor", "bak", "node_modules", "public"]
|
||||||
|
include_ext = ["go", "md", "html"]
|
||||||
|
kill_delay = "0s"
|
||||||
|
log = "build-errors.log"
|
||||||
|
|
||||||
|
[color]
|
||||||
|
app = ""
|
||||||
|
build = "yellow"
|
||||||
|
main = "magenta"
|
||||||
|
runner = "green"
|
||||||
|
watcher = "cyan"
|
||||||
|
|
||||||
|
[log]
|
||||||
|
main_only = false
|
||||||
|
silent = false
|
||||||
|
time = false
|
||||||
|
|
||||||
|
[misc]
|
||||||
|
clean_on_exit = false
|
||||||
|
|
||||||
|
[proxy]
|
||||||
|
app_port = 0
|
||||||
|
enabled = false
|
||||||
|
proxy_port = 0
|
||||||
|
|
||||||
|
[screen]
|
||||||
|
clear_on_rebuild = false
|
||||||
|
keep_scroll = true
|
||||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
node_modules/
|
||||||
|
bak/
|
||||||
|
tmp/
|
||||||
|
*.log
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
# About
|
---
|
||||||
|
title: Go SSG - About
|
||||||
|
navTitle: About
|
||||||
|
description: More About My Go SSG Site.
|
||||||
|
date: 2025-04-19
|
||||||
|
navPosition: 2
|
||||||
|
---
|
||||||
|
|
||||||
Experimenting with Go. Seems pretty cool so far!
|
Experimenting with Go. Seems pretty cool so far!
|
||||||
|
|||||||
20
content/blog/another-blog-post.md
Normal file
20
content/blog/another-blog-post.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
title: Go SSG - Another Blog Post
|
||||||
|
description: A longer summary of the post. Lorem ipsum dolor set amit.
|
||||||
|
date: 2025-04-19
|
||||||
|
categories:
|
||||||
|
- go
|
||||||
|
- anger
|
||||||
|
---
|
||||||
|
|
||||||
|
## Why isn't this working?! 😭
|
||||||
|
|
||||||
|
This is my first Go SSG blog post.
|
||||||
|
|
||||||
|
This project is a simple static site generator written in Go. It is designed to be easy to use and extend, allowing you to create a static website quickly and efficiently.
|
||||||
|
|
||||||
|
It supports features like:
|
||||||
|
|
||||||
|
- Markdown content
|
||||||
|
- Customizable templates
|
||||||
|
- Simple configuration
|
||||||
20
content/blog/my-first-blog-post.md
Normal file
20
content/blog/my-first-blog-post.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
title: Go SSG - My First Blog Post
|
||||||
|
description: A short summary of the post.
|
||||||
|
date: 2025-04-19
|
||||||
|
categories:
|
||||||
|
- progrmming
|
||||||
|
- go
|
||||||
|
---
|
||||||
|
|
||||||
|
## *Tap Tap Tap* Is this thing on?
|
||||||
|
|
||||||
|
<p class="text-primary">This is my first Go SSG blog post.</p>
|
||||||
|
|
||||||
|
This project is a simple static site generator written in Go. It is designed to be easy to use and extend, allowing you to create a static website quickly and efficiently.
|
||||||
|
|
||||||
|
It supports features like:
|
||||||
|
|
||||||
|
- Markdown content
|
||||||
|
- Customizable templates
|
||||||
|
- Simple configuration
|
||||||
5
content/contact.md
Normal file
5
content/contact.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
title: Contact
|
||||||
|
navTitle: Contact
|
||||||
|
navPosition: 4
|
||||||
|
---
|
||||||
@@ -1,3 +1,10 @@
|
|||||||
# Welcome
|
---
|
||||||
|
title: Go SSG - Home
|
||||||
|
navTitle: Home
|
||||||
|
showInNav: true
|
||||||
|
navPosition: 1
|
||||||
|
description: My Go SSG Site.
|
||||||
|
date: 2025-04-19
|
||||||
|
---
|
||||||
|
|
||||||
This is your first static site built with Go!
|
Welcome to my static site built with Go!
|
||||||
|
|||||||
5
content/thanks.md
Normal file
5
content/thanks.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
title: Thank You
|
||||||
|
navTitle: ""
|
||||||
|
showInNav: false
|
||||||
|
---
|
||||||
32
css/base.css
Normal file
32
css/base.css
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
@import './global.css';
|
||||||
|
@import './colors.css';
|
||||||
|
@import './typography.css';
|
||||||
|
@import './prose.css';
|
||||||
|
|
||||||
|
/* Import Tailwind typography plugin */
|
||||||
|
@plugin "@tailwindcss/typography";
|
||||||
|
|
||||||
|
body {
|
||||||
|
@apply bg-background text-text font-sans;
|
||||||
|
}
|
||||||
|
|
||||||
|
header#site_head {
|
||||||
|
@apply bg-primary text-white flex items-center justify-between;
|
||||||
|
@apply py-4 px-60;
|
||||||
|
|
||||||
|
nav ul {
|
||||||
|
@apply flex space-x-4 justify-end-safe prose-a:text-white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
article, div.main {
|
||||||
|
@apply prose container mx-auto px-4 py-8 max-w-5xl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer#site_foot {
|
||||||
|
@apply bg-secondary text-white text-center m-0 p-0;
|
||||||
|
}
|
||||||
38
css/colors.css
Normal file
38
css/colors.css
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/* Theme color definitions */
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--color-black: oklch(0% 0 0);
|
||||||
|
--color-white: oklch(100% 0 0);
|
||||||
|
|
||||||
|
--color-background: oklch(89.75% 0 0);
|
||||||
|
--color-text: oklch(0% 0 0);
|
||||||
|
|
||||||
|
--color-primary: oklch(60.48% 0.2166 257.2);
|
||||||
|
--color-primary-100: color-mix(in oklch, var(--color-primary) 10%, white);
|
||||||
|
--color-primary-200: color-mix(in oklch, var(--color-primary) 20%, white);
|
||||||
|
--color-primary-300: color-mix(in oklch, var(--color-primary) 30%, white);
|
||||||
|
--color-primary-400: color-mix(in oklch, var(--color-primary) 40%, white);
|
||||||
|
--color-primary-500: color-mix(in oklch, var(--color-primary) 50%, white);
|
||||||
|
--color-primary-600: color-mix(in oklch, var(--color-primary) 60%, white);
|
||||||
|
--color-primary-700: color-mix(in oklch, var(--color-primary) 70%, white);
|
||||||
|
--color-primary-800: color-mix(in oklch, var(--color-primary) 80%, white);
|
||||||
|
--color-primary-900: color-mix(in oklch, var(--color-primary) 90%, white);
|
||||||
|
|
||||||
|
--color-secondary: oklch(55.75% 0.0165 244.9);
|
||||||
|
--color-secondary-100: color-mix(in oklch, var(--color-secondary) 10%, white);
|
||||||
|
--color-secondary-200: color-mix(in oklch, var(--color-secondary) 20%, white);
|
||||||
|
--color-secondary-300: color-mix(in oklch, var(--color-secondary) 30%, white);
|
||||||
|
--color-secondary-400: color-mix(in oklch, var(--color-secondary) 40%, white);
|
||||||
|
--color-secondary-500: color-mix(in oklch, var(--color-secondary) 50%, white);
|
||||||
|
--color-secondary-600: color-mix(in oklch, var(--color-secondary) 60%, white);
|
||||||
|
--color-secondary-700: color-mix(in oklch, var(--color-secondary) 70%, white);
|
||||||
|
--color-secondary-800: color-mix(in oklch, var(--color-secondary) 80%, white);
|
||||||
|
--color-secondary-900: color-mix(in oklch, var(--color-secondary) 90%, white);
|
||||||
|
|
||||||
|
--color-success: oklch(64.01% 0.1751 146.7);
|
||||||
|
--color-info: oklch(65.52% 0.1105 212.2);
|
||||||
|
--color-warning: oklch(84.42% 0.1722 84.93);
|
||||||
|
--color-danger: oklch(59.15% 0.202 21.24);
|
||||||
|
--color-light: oklch(98.16% 0.0017 247.8);
|
||||||
|
--color-dark: oklch(34.51% 0.0133 248.2);
|
||||||
|
}
|
||||||
84
css/global.css
Normal file
84
css/global.css
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/* Miscellaneous custom styles */
|
||||||
|
@theme {
|
||||||
|
--spacing-menu-top: calc(100% + .9375rem);
|
||||||
|
--spacing-section: 2rem;
|
||||||
|
|
||||||
|
--shadow-menu-shadow: 0 .25rem .375rem rgba(0,0,0,0.1);
|
||||||
|
|
||||||
|
/** Breakpoints
|
||||||
|
* The breakpoints are set to match the default Tailwind breakpoints.
|
||||||
|
* You can override them here if you want to use different breakpoints.
|
||||||
|
*
|
||||||
|
* @see https://tailwindcss.com/docs/breakpoints
|
||||||
|
*/
|
||||||
|
--breakpoint-*: initial;
|
||||||
|
--breakpoint-xxs: 22.5rem; /* 360px */
|
||||||
|
--breakpoint-xs: 29.6875rem; /* 475px */
|
||||||
|
--breakpoint-sm: 40rem; /* 640px */
|
||||||
|
--breakpoint-md: 48rem; /* 768px */
|
||||||
|
--breakpoint-lg: 64rem; /* 1024px */
|
||||||
|
--breakpoint-xl: 80rem; /* 1280px */
|
||||||
|
--breakpoint-2xl: 96rem; /* 1536px */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Basic layout styles */
|
||||||
|
body, html {
|
||||||
|
background-color: var(--color-background);
|
||||||
|
color: var(--color-text);
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
@apply relative my-section px-section;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
@apply mt-0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child, p:last-child {
|
||||||
|
@apply mb-0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.has-background {
|
||||||
|
@apply py-section bg-cover bg-no-repeat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Allows containers inside containers
|
||||||
|
*
|
||||||
|
* .container .wp-block-section {
|
||||||
|
* @apply mx-break-out;
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
.content-wrapper {
|
||||||
|
.alignfull {
|
||||||
|
@apply max-w-full;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alignwide {
|
||||||
|
@apply max-w-full;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alignleft {
|
||||||
|
@apply ml-0 mr-auto float-none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alignright {
|
||||||
|
@apply ml-auto mr-0 float-none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aligncenter {
|
||||||
|
@apply mx-auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive embeds */
|
||||||
|
.embed { position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden; }
|
||||||
|
.embed iframe, .embed object, .embed embed, .embed video { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
|
||||||
36
css/prose.css
Normal file
36
css/prose.css
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/* Theme prose styles */
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--tw-prose-body: var(--color-primary);
|
||||||
|
--tw-prose-headings: var(--color-primary);
|
||||||
|
--tw-prose-lead: var(--color-primary);
|
||||||
|
--tw-prose-links: var(--color-info);
|
||||||
|
--tw-prose-bold: var(--color-primary);
|
||||||
|
--tw-prose-counters: var(--color-primary);
|
||||||
|
--tw-prose-bullets: var(--color-secondary);
|
||||||
|
--tw-prose-hr: var(--color-secondary);
|
||||||
|
--tw-prose-quotes: var(--color-primary);
|
||||||
|
--tw-prose-quote-borders: var(--color-primary);
|
||||||
|
--tw-prose-captions: var(--color-secondary);
|
||||||
|
--tw-prose-code: var(--color-primary);
|
||||||
|
--tw-prose-pre-code: var(--color-primary);
|
||||||
|
--tw-prose-pre-bg: var(--color-secondary);
|
||||||
|
--tw-prose-th-borders: var(--color-secondary);
|
||||||
|
--tw-prose-td-borders: var(--color-secondary);
|
||||||
|
--tw-prose-invert-body: var(--color-primary);
|
||||||
|
--tw-prose-invert-headings: var(--color-primary);
|
||||||
|
--tw-prose-invert-lead: var(--color-primary);
|
||||||
|
--tw-prose-invert-links: var(--color-secondary);
|
||||||
|
--tw-prose-invert-bold: var(--color-primary);
|
||||||
|
--tw-prose-invert-counters: var(--color-primary);
|
||||||
|
--tw-prose-invert-bullets: var(--color-primary);
|
||||||
|
--tw-prose-invert-hr: var(--color-secondary);
|
||||||
|
--tw-prose-invert-quotes: var(--color-primary);
|
||||||
|
--tw-prose-invert-quote-borders: var(--color-primary);
|
||||||
|
--tw-prose-invert-captions: var(--color-primary);
|
||||||
|
--tw-prose-invert-code: var(--color-secondary);
|
||||||
|
--tw-prose-invert-pre-code: var(--color-primary);
|
||||||
|
--tw-prose-invert-pre-bg: oklch(0% 0 0 / 50%);
|
||||||
|
--tw-prose-invert-th-borders: var(--color-primary);
|
||||||
|
--tw-prose-invert-td-borders: var(--color-primary);
|
||||||
|
}
|
||||||
158
css/typography.css
Normal file
158
css/typography.css
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
/* Basic typographical styles */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All font sizes are based on 16px base font size and 1920px wide screen
|
||||||
|
* Default size is expressed as percentage of screen width.
|
||||||
|
* text-14px: 12px-27px, default: 14px
|
||||||
|
* text-16px: 14px-28px, default: 16px
|
||||||
|
* text-18px: 14px-30px, default: 18px
|
||||||
|
* text-20px: 16px-32px, default: 20px
|
||||||
|
* text-22px: 17px-33px, default: 22px
|
||||||
|
* text-25px: 18px-35px, default: 25px
|
||||||
|
* text-30px: 19px-37px, default: 30px
|
||||||
|
* text-35px: 20px-40px, default: 35px
|
||||||
|
* text-38px: 22px-48px, default: 38px
|
||||||
|
* text-40px: 24px-56px, default: 40px
|
||||||
|
* text-45px: 25px-64px, default: 45px
|
||||||
|
* text-50px: 27px-72px, default: 50px
|
||||||
|
* text-55px: 28px-76px, default: 55px
|
||||||
|
* text-60px: 30px-80px, default: 60px
|
||||||
|
* text-70px: 30px-76px, default: 70px
|
||||||
|
* text-75px: 32px-80px, default: 75px
|
||||||
|
*/
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--font-sans: "Raleway", sans-serif;
|
||||||
|
--line-height: 1.6;
|
||||||
|
|
||||||
|
--text-base: 1rem;
|
||||||
|
--text-14px: clamp(0.75rem, 0.7292vw, 1.7rem);
|
||||||
|
--text-16px: clamp(0.875rem, 0.8333vw, 1.8rem);
|
||||||
|
--text-18px: clamp(0.875rem, 0.9375vw, 1.9rem);
|
||||||
|
--text-20px: clamp(1rem, 1.0417vw, 2rem);
|
||||||
|
--text-22px: clamp(1.1rem, 1.15vw, 2.1rem);
|
||||||
|
--text-25px: clamp(1.125rem, 1.3021vw, 2.2rem);
|
||||||
|
--text-30px: clamp(1.185rem, 1.5625vw, 2.35rem);
|
||||||
|
--text-35px: clamp(1.25rem, 1.8229vw, 2.5rem);
|
||||||
|
--text-38px: clamp(1.4rem, 1.9791vw, 3rem);
|
||||||
|
--text-40px: clamp(1.5rem, 2.0834vw, 3.5rem);
|
||||||
|
--text-45px: clamp(1.6rem, 2.3438vw, 4rem);
|
||||||
|
--text-50px: clamp(1.7rem, 2.6042vw, 4.5rem);
|
||||||
|
--text-70px: clamp(1.9rem, 3.6458vw, 4.8rem);
|
||||||
|
--text-75px: clamp(2rem, 3.9063vw, 5rem);
|
||||||
|
|
||||||
|
--h1: calc(var(--text-base) * 2.25);
|
||||||
|
--h2: calc(var(--text-base) * 1.75);
|
||||||
|
--h3: calc(var(--text-base) * 1.5);
|
||||||
|
--h4: calc(var(--text-base) * 1.25);
|
||||||
|
--h5: calc(var(--text-base) * 1.125);
|
||||||
|
--h6: calc(var(--text-base) * 1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: white;
|
||||||
|
color: black;
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-size: var(--text-base);
|
||||||
|
line-height: var(--line-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection { background: var(--color-warning); }
|
||||||
|
|
||||||
|
@layer components {
|
||||||
|
h1, h2, h3,
|
||||||
|
h4, h5, h6 {
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, .h1 {
|
||||||
|
font-size: var(--h1);
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2, .h2 {
|
||||||
|
font-size: var(--h2);
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3, .h3 {
|
||||||
|
font-size: var(--h3);
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4, .h4 {
|
||||||
|
font-size: var(--h4);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5, .h5 { font-size: var(--h5); }
|
||||||
|
|
||||||
|
h6, .h6 { font-size: var(--h6); }
|
||||||
|
}
|
||||||
|
|
||||||
|
a, .link {
|
||||||
|
color: var(--color-info);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 200ms;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover { color: var(--color-primary); }
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 a, .h1 a,
|
||||||
|
h2 a, .h2 a,
|
||||||
|
h3 a, .h3 a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#site_head h1 a {
|
||||||
|
@apply text-white text-40px hover:text-primary-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
li ul, li ol { margin: 0 1rem; }
|
||||||
|
|
||||||
|
ul { list-style-type: disc; }
|
||||||
|
|
||||||
|
ol { list-style-type: decimal; }
|
||||||
|
|
||||||
|
ol ol { list-style: lower-alpha; }
|
||||||
|
|
||||||
|
ol ol ol { list-style: lower-roman; }
|
||||||
|
|
||||||
|
ol ol ol ol { list-style: lower-alpha; }
|
||||||
|
|
||||||
|
pre, code,
|
||||||
|
samp, style { font-family: monospace; }
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre code {
|
||||||
|
background-color: inherit;
|
||||||
|
border-radius: 0;
|
||||||
|
color: inherit;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
@apply bg-black/40 px-[3px] py-[2px] font-mono text-light text-xs rounded-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
background-color: black;
|
||||||
|
border: none;
|
||||||
|
display: block;
|
||||||
|
height: 1px;
|
||||||
|
margin: 1rem 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
5
go.mod
5
go.mod
@@ -2,4 +2,7 @@ module go-ssg
|
|||||||
|
|
||||||
go 1.24.2
|
go 1.24.2
|
||||||
|
|
||||||
require github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
require (
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
|
|||||||
3
go.sum
3
go.sum
@@ -1,2 +1,5 @@
|
|||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
376
main.go
376
main.go
@@ -1,89 +1,167 @@
|
|||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"sort"
|
||||||
|
"io/fs"
|
||||||
|
|
||||||
"github.com/russross/blackfriday/v2"
|
"github.com/russross/blackfriday/v2"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NavItem represents a link in the navigation menu
|
|
||||||
type NavItem struct {
|
type NavItem struct {
|
||||||
Title string
|
Title string
|
||||||
URL string
|
URL string
|
||||||
|
Position int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PageMeta struct {
|
||||||
|
Title string `yaml:"title"`
|
||||||
|
NavTitle string `yaml:"navTitle"`
|
||||||
|
ShowInNav *bool `yaml:"showInNav"`
|
||||||
|
NavPosition *int `yaml:"navPosition"`
|
||||||
|
Description string `yaml:"description"`
|
||||||
|
Date string `yaml:"date"`
|
||||||
|
FormattedDate string `yaml:"-"`
|
||||||
|
Categories []string `yaml:"categories"`
|
||||||
|
Slug string
|
||||||
|
}
|
||||||
|
|
||||||
|
var slugRegex = regexp.MustCompile(`[^a-z0-9]+`)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
contentDir := "./content"
|
contentDir := "./content"
|
||||||
outputDir := "./public"
|
outputDir := "./public"
|
||||||
templateFile := "./templates/layout.html"
|
|
||||||
|
|
||||||
tpl, err := parseTemplate(templateFile)
|
tpl, err := loadTemplates()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Template parsing error: %v\n", err)
|
fmt.Printf("Template parsing error: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
files, err := readContentFiles(contentDir)
|
files, err := ioutil.ReadDir(contentDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Failed to read content directory: %v\n", err)
|
fmt.Printf("Failed to read content directory: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
nav := buildNavigation(files)
|
entries, _ := os.ReadDir(contentDir)
|
||||||
|
nav := buildNav(entries, contentDir)
|
||||||
for _, file := range files {
|
renderStaticPages(files, contentDir, outputDir, tpl, nav)
|
||||||
if filepath.Ext(file.Name()) == ".md" {
|
blogPosts, categoryMap := processBlogPosts(contentDir, outputDir, tpl, nav)
|
||||||
err := generateHTML(file, contentDir, outputDir, tpl, nav)
|
renderContactPage(contentDir, outputDir, tpl, nav)
|
||||||
if err != nil {
|
renderThanksPage(contentDir, outputDir, tpl, nav)
|
||||||
fmt.Printf("Error generating HTML for %s: %v\n", file.Name(), err)
|
buildBlogIndex(blogPosts, tpl, outputDir, nav)
|
||||||
}
|
buildCategoryPages(categoryMap, tpl, outputDir, nav)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("✅ Site generation complete.")
|
fmt.Println("✅ Site generation complete.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseTemplate(templateFile string) (*template.Template, error) {
|
func loadTemplates() (*template.Template, error) {
|
||||||
return template.ParseFiles(templateFile)
|
return template.New("").Funcs(templateFuncs()).ParseGlob("templates/*.html")
|
||||||
}
|
}
|
||||||
|
|
||||||
func readContentFiles(contentDir string) ([]os.FileInfo, error) {
|
func buildNav(entries []fs.DirEntry, contentDir string) []NavItem {
|
||||||
return ioutil.ReadDir(contentDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildNavigation(files []os.FileInfo) []NavItem {
|
|
||||||
var nav []NavItem
|
var nav []NavItem
|
||||||
for _, file := range files {
|
|
||||||
if filepath.Ext(file.Name()) == ".md" {
|
for _, entry := range entries {
|
||||||
name := strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))
|
if entry.Type().IsDir() || !strings.HasSuffix(entry.Name(), ".md") {
|
||||||
title := strings.Title(name)
|
continue
|
||||||
url := "/"
|
|
||||||
if name != "index" {
|
|
||||||
url = "/" + name + "/"
|
|
||||||
}
|
|
||||||
nav = append(nav, NavItem{Title: title, URL: url})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
name := strings.TrimSuffix(entry.Name(), filepath.Ext(entry.Name()))
|
||||||
|
path := filepath.Join(contentDir, entry.Name())
|
||||||
|
|
||||||
|
rawContent, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to read %s: %v\n", entry.Name(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
meta, _ := parseFrontMatter(rawContent)
|
||||||
|
|
||||||
|
// Skip pages with showInNav: false
|
||||||
|
if meta.ShowInNav != nil && !*meta.ShowInNav {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pull title info
|
||||||
|
title := strings.TrimSpace(meta.NavTitle)
|
||||||
|
if title == "" {
|
||||||
|
title = meta.Title
|
||||||
|
}
|
||||||
|
if title == "" && name == "index" {
|
||||||
|
title = "Home"
|
||||||
|
} else if title == "" {
|
||||||
|
title = strings.Title(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build URL
|
||||||
|
url := "/"
|
||||||
|
if name != "index" {
|
||||||
|
url = "/" + name + "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pull nav position regardless of title logic
|
||||||
|
position := 999
|
||||||
|
if meta.NavPosition != nil {
|
||||||
|
position = *meta.NavPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
nav = append(nav, NavItem{
|
||||||
|
Title: title,
|
||||||
|
URL: url,
|
||||||
|
Position: position,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Optional: add Blog manually (or skip if you handle it as a page)
|
||||||
|
nav = append(nav, NavItem{
|
||||||
|
Title: "Blog",
|
||||||
|
URL: "/blog/",
|
||||||
|
Position: 3,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Sort by position
|
||||||
|
sort.SliceStable(nav, func(i, j int) bool {
|
||||||
|
return nav[i].Position < nav[j].Position
|
||||||
|
})
|
||||||
|
|
||||||
return nav
|
return nav
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateHTML(file os.FileInfo, contentDir, outputDir string, tpl *template.Template, nav []NavItem) error {
|
func renderStaticPages(files []os.FileInfo, contentDir, outputDir string, tpl *template.Template, nav []NavItem) {
|
||||||
|
for _, file := range files {
|
||||||
|
if filepath.Ext(file.Name()) == ".md" {
|
||||||
|
renderStaticPage(file, contentDir, outputDir, tpl, nav)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderStaticPage(file os.FileInfo, contentDir, outputDir string, tpl *template.Template, nav []NavItem) {
|
||||||
name := strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))
|
name := strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))
|
||||||
title := strings.Title(name)
|
title := strings.Title(name)
|
||||||
|
|
||||||
mdPath := filepath.Join(contentDir, file.Name())
|
mdPath := filepath.Join(contentDir, file.Name())
|
||||||
mdContent, err := ioutil.ReadFile(mdPath)
|
rawContent, err := ioutil.ReadFile(mdPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read %s: %w", file.Name(), err)
|
fmt.Printf("Failed to read %s: %v\n", file.Name(), err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
htmlContent := blackfriday.Run(mdContent)
|
meta, content := parseFrontMatter(rawContent)
|
||||||
|
htmlContent := blackfriday.Run(content)
|
||||||
|
if meta.Title == "" {
|
||||||
|
meta.Title = title
|
||||||
|
}
|
||||||
|
|
||||||
var outPath string
|
var outPath string
|
||||||
if name == "index" {
|
if name == "index" {
|
||||||
@@ -96,17 +174,219 @@ func generateHTML(file os.FileInfo, contentDir, outputDir string, tpl *template.
|
|||||||
|
|
||||||
outFile, err := os.Create(outPath)
|
outFile, err := os.Create(outPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create %s: %w", outPath, err)
|
fmt.Printf("Failed to create %s: %v\n", outPath, err)
|
||||||
}
|
return
|
||||||
defer outFile.Close()
|
|
||||||
|
|
||||||
data := map[string]interface{}{
|
|
||||||
"Content": template.HTML(htmlContent),
|
|
||||||
"Title": title,
|
|
||||||
"Nav": nav,
|
|
||||||
"Year": time.Now().Year(),
|
|
||||||
"Date": time.Now().Format("January 2, 2006"),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return tpl.Execute(outFile, data)
|
tpl.ExecuteTemplate(outFile, "static_page", map[string]interface{}{
|
||||||
|
"Title": meta.Title,
|
||||||
|
"Description": meta.Description,
|
||||||
|
"Date": meta.Date,
|
||||||
|
"Categories": meta.Categories,
|
||||||
|
"Content": template.HTML(htmlContent),
|
||||||
|
"Nav": nav,
|
||||||
|
"Year": time.Now().Year(),
|
||||||
|
"PageTemplate": "static",
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Printf("Generated: %s\n", outPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderContactPage(contentDir, outputDir string, tpl *template.Template, nav []NavItem) {
|
||||||
|
contactPath := filepath.Join(contentDir, "contact.md")
|
||||||
|
rawContent, err := os.ReadFile(contactPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Contact page not found: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
meta, _ := parseFrontMatter(rawContent)
|
||||||
|
|
||||||
|
outPath := filepath.Join(outputDir, "contact", "index.html")
|
||||||
|
os.MkdirAll(filepath.Dir(outPath), os.ModePerm)
|
||||||
|
|
||||||
|
outFile, err := os.Create(outPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to create contact page: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tpl.ExecuteTemplate(outFile, "contact_page", map[string]interface{}{
|
||||||
|
"Title": meta.Title,
|
||||||
|
"Description": meta.Description,
|
||||||
|
"Nav": nav,
|
||||||
|
"Year": time.Now().Year(),
|
||||||
|
"PageTemplate": "contact_page",
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println("Generated: /contact/")
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderThanksPage(contentDir, outputDir string, tpl *template.Template, nav []NavItem) {
|
||||||
|
path := filepath.Join(contentDir, "thanks.md")
|
||||||
|
rawContent, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Thanks page not found: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
meta, _ := parseFrontMatter(rawContent)
|
||||||
|
|
||||||
|
outPath := filepath.Join(outputDir, "thanks", "index.html")
|
||||||
|
os.MkdirAll(filepath.Dir(outPath), os.ModePerm)
|
||||||
|
|
||||||
|
outFile, err := os.Create(outPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to create thanks page: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tpl.ExecuteTemplate(outFile, "thanks_page", map[string]interface{}{
|
||||||
|
"Title": meta.Title,
|
||||||
|
"Description": meta.Description,
|
||||||
|
"Nav": nav,
|
||||||
|
"Year": time.Now().Year(),
|
||||||
|
"PageTemplate": "thanks_page",
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println("Generated: /thanks/")
|
||||||
|
}
|
||||||
|
|
||||||
|
func processBlogPosts(contentDir, outputDir string, tpl *template.Template, nav []NavItem) ([]PageMeta, map[string][]PageMeta) {
|
||||||
|
var blogPosts []PageMeta
|
||||||
|
categoryMap := make(map[string][]PageMeta)
|
||||||
|
|
||||||
|
blogDir := filepath.Join(contentDir, "blog")
|
||||||
|
blogFiles, _ := ioutil.ReadDir(blogDir)
|
||||||
|
|
||||||
|
for _, file := range blogFiles {
|
||||||
|
if filepath.Ext(file.Name()) != ".md" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
processBlogPost(file, blogDir, outputDir, tpl, nav, &blogPosts, categoryMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
return blogPosts, categoryMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func processBlogPost(file os.FileInfo, blogDir, outputDir string, tpl *template.Template, nav []NavItem, blogPosts *[]PageMeta, categoryMap map[string][]PageMeta) {
|
||||||
|
slug := strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))
|
||||||
|
blogPath := filepath.Join(blogDir, file.Name())
|
||||||
|
rawContent, err := ioutil.ReadFile(blogPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to read blog post %s: %v\n", file.Name(), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
meta, content := parseFrontMatter(rawContent)
|
||||||
|
html := blackfriday.Run(content)
|
||||||
|
meta.Slug = slug
|
||||||
|
if meta.Title == "" {
|
||||||
|
meta.Title = strings.Title(slug)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cat := range meta.Categories {
|
||||||
|
slug := slugify(cat)
|
||||||
|
categoryMap[slug] = append(categoryMap[slug], meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
outPath := filepath.Join(outputDir, "blog", slug, "index.html")
|
||||||
|
os.MkdirAll(filepath.Dir(outPath), os.ModePerm)
|
||||||
|
|
||||||
|
outFile, err := os.Create(outPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to create blog file %s: %v\n", outPath, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tpl.ExecuteTemplate(outFile, "blog_post_page", map[string]interface{}{
|
||||||
|
"Title": meta.Title,
|
||||||
|
"Description": meta.Description,
|
||||||
|
"Date": meta.Date,
|
||||||
|
"Categories": meta.Categories,
|
||||||
|
"Content": template.HTML(html),
|
||||||
|
"Nav": nav,
|
||||||
|
"Year": time.Now().Year(),
|
||||||
|
"PageTemplate": "blog_post",
|
||||||
|
})
|
||||||
|
|
||||||
|
meta.FormattedDate = meta.Date.Format(time.RFC1123Z)
|
||||||
|
|
||||||
|
*blogPosts = append(*blogPosts, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildCategoryPages(categoryMap map[string][]PageMeta, tpl *template.Template, outputDir string, nav []NavItem) {
|
||||||
|
for catSlug, posts := range categoryMap {
|
||||||
|
outDir := filepath.Join(outputDir, "blog", "category", catSlug)
|
||||||
|
os.MkdirAll(outDir, os.ModePerm)
|
||||||
|
|
||||||
|
outPath := filepath.Join(outDir, "index.html")
|
||||||
|
outFile, err := os.Create(outPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to create category page for %s: %v\n", catSlug, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tpl.ExecuteTemplate(outFile, "category_page", map[string]interface{}{
|
||||||
|
"Title": "Category: " + strings.Title(strings.ReplaceAll(catSlug, "-", " ")),
|
||||||
|
"Posts": posts,
|
||||||
|
"Nav": nav,
|
||||||
|
"Year": time.Now().Year(),
|
||||||
|
"PageTemplate": "category_page",
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Printf("Generated category: /blog/category/%s/\n", catSlug)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseFrontMatter(raw []byte) (PageMeta, []byte) {
|
||||||
|
var meta PageMeta
|
||||||
|
raw = bytes.ReplaceAll(raw, []byte("\r\n"), []byte("\n"))
|
||||||
|
|
||||||
|
if bytes.HasPrefix(raw, []byte("---\n")) {
|
||||||
|
parts := bytes.SplitN(raw, []byte("---\n"), 3)
|
||||||
|
if len(parts) == 3 {
|
||||||
|
err := yaml.Unmarshal(parts[1], &meta)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("⚠️ Failed to parse front matter: %v\n", err)
|
||||||
|
}
|
||||||
|
return meta, parts[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return meta, raw
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildBlogIndex(posts []PageMeta, tpl *template.Template, outputDir string, nav []NavItem) {
|
||||||
|
outPath := filepath.Join(outputDir, "blog", "index.html")
|
||||||
|
os.MkdirAll(filepath.Dir(outPath), os.ModePerm)
|
||||||
|
|
||||||
|
outFile, err := os.Create(outPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to create blog index:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tpl.ExecuteTemplate(outFile, "blog_index_page", map[string]interface{}{
|
||||||
|
"Title": "Blog Index",
|
||||||
|
"Posts": posts,
|
||||||
|
"Nav": nav,
|
||||||
|
"Year": time.Now().Year(),
|
||||||
|
"PageTemplate": "blog_index",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func slugify(s string) string {
|
||||||
|
s = strings.ToLower(s)
|
||||||
|
s = slugRegex.ReplaceAllString(s, "-")
|
||||||
|
return strings.Trim(s, "-")
|
||||||
|
}
|
||||||
|
|
||||||
|
func templateFuncs() template.FuncMap {
|
||||||
|
return template.FuncMap{
|
||||||
|
"lower": strings.ToLower,
|
||||||
|
"slugify": slugify,
|
||||||
|
"urlquery": template.URLQueryEscaper,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1427
package-lock.json
generated
Normal file
1427
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
package.json
Normal file
21
package.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "ssg",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "concurrently -k -n TAILWIND,GO,SERVE -c blue,green,magenta \"npm run tailwind\" \"npm run go\" \"npm run serve\"",
|
||||||
|
"tailwind": "npx @tailwindcss/cli -i ./css/base.css -o ./public/assets/style.css --watch",
|
||||||
|
"go": "air",
|
||||||
|
"serve": "live-server public"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "",
|
||||||
|
"devDependencies": {
|
||||||
|
"@tailwindcss/cli": "^4.1.4",
|
||||||
|
"@tailwindcss/typography": "^0.5.16",
|
||||||
|
"concurrently": "^9.1.2",
|
||||||
|
"tailwindcss": "^4.1.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,35 +1,48 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>About</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<nav>
|
|
||||||
<ul>
|
|
||||||
|
|
||||||
<li><a href="/about/">About</a></li>
|
|
||||||
|
|
||||||
<li><a href="/">Index</a></li>
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<h1>About
|
|
||||||
|
|
||||||
|
|
||||||
<p>
|
<!DOCTYPE html>
|
||||||
Experimenting with Go. Seems pretty cool so far!
|
<html lang="en">
|
||||||
</p>
|
|
||||||
|
<head>
|
||||||
</main>
|
<meta charset="UTF-8">
|
||||||
|
<title>Go SSG - About</title>
|
||||||
<footer>
|
<link rel="stylesheet" href="/assets/style.css">
|
||||||
<p>© 2025 Keith Solomon</p>
|
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon">
|
||||||
</footer>
|
</head>
|
||||||
</body>
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header id="site_head">
|
||||||
|
<h1><a href="/">Go SSG</a></h1>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<ul class="list-none">
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/">Home</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/about/">About</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/blog/">Blog</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/contact/">Contact</a></li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<article>
|
||||||
|
<h1>Go SSG - About</h1>
|
||||||
|
<p>Experimenting with Go. Seems pretty cool so far!</p>
|
||||||
|
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
<footer id="site_foot">
|
||||||
|
<p class="p-0 py-4 m-0 leading-none">© 2025 Keith Solomon - Go SSG</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|||||||
41
public/assets/contact.js
Normal file
41
public/assets/contact.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
|
||||||
|
document.getElementById("contactForm").addEventListener("submit", function (e) {
|
||||||
|
const name = document.getElementById("name");
|
||||||
|
const email = document.getElementById("email");
|
||||||
|
const message = document.getElementById("message");
|
||||||
|
let valid = true;
|
||||||
|
|
||||||
|
// Clear errors
|
||||||
|
document.querySelectorAll("[data-error]").forEach(el => {
|
||||||
|
el.classList.add("hidden");
|
||||||
|
el.textContent = "";
|
||||||
|
});
|
||||||
|
|
||||||
|
// Validate name
|
||||||
|
if (name.value.trim() === "") {
|
||||||
|
showError("name", "Name is required");
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate email
|
||||||
|
if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email.value)) {
|
||||||
|
showError("email", "Enter a valid email address");
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate message
|
||||||
|
if (message.value.trim().length < 10) {
|
||||||
|
showError("message", "Message must be at least 10 characters");
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!valid) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
function showError(field, message) {
|
||||||
|
const el = document.querySelector(`[data-error="\${field}"]`);
|
||||||
|
el.textContent = message;
|
||||||
|
el.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
});
|
||||||
BIN
public/assets/favicon.ico
Normal file
BIN
public/assets/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 106 KiB |
1021
public/assets/style.css
Normal file
1021
public/assets/style.css
Normal file
File diff suppressed because it is too large
Load Diff
75
public/blog/another-blog-post/index.html
Normal file
75
public/blog/another-blog-post/index.html
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Go SSG - Another Blog Post</title>
|
||||||
|
<link rel="stylesheet" href="/assets/style.css">
|
||||||
|
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header id="site_head">
|
||||||
|
<h1><a href="/">Go SSG</a></h1>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<ul class="list-none">
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/">Home</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/about/">About</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/blog/">Blog</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/contact/">Contact</a></li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<article>
|
||||||
|
<h1>Go SSG - Another Blog Post</h1>
|
||||||
|
<p><small>2025-04-19</small></p>
|
||||||
|
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Categories:
|
||||||
|
|
||||||
|
|
||||||
|
<a href="/blog/category/go/">go</a>
|
||||||
|
|
||||||
|
,
|
||||||
|
<a href="/blog/category/anger/">anger</a>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Why isn’t this working?! 😭</h2>
|
||||||
|
|
||||||
|
<p>This is my first Go SSG blog post.</p>
|
||||||
|
|
||||||
|
<p>This project is a simple static site generator written in Go. It is designed to be easy to use and extend, allowing you to create a static website quickly and efficiently.</p>
|
||||||
|
|
||||||
|
<p>It supports features like:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Markdown content</li>
|
||||||
|
<li>Customizable templates</li>
|
||||||
|
<li>Simple configuration</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
<footer id="site_foot">
|
||||||
|
<p class="p-0 py-4 m-0 leading-none">© 2025 Keith Solomon - Go SSG</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
49
public/blog/category/anger/index.html
Normal file
49
public/blog/category/anger/index.html
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Category: Anger</title>
|
||||||
|
<link rel="stylesheet" href="/assets/style.css">
|
||||||
|
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header id="site_head">
|
||||||
|
<h1><a href="/">Go SSG</a></h1>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<ul class="list-none">
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/">Home</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/about/">About</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/blog/">Blog</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/contact/">Contact</a></li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<h1>Category: Anger</h1>
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<li><a href="/blog/another-blog-post/">Go SSG - Another Blog Post</a><br>A longer summary of the post. Lorem ipsum dolor set amit.</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
<footer id="site_foot">
|
||||||
|
<p class="p-0 py-4 m-0 leading-none">© 2025 Keith Solomon - Go SSG</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
51
public/blog/category/go/index.html
Normal file
51
public/blog/category/go/index.html
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Category: Go</title>
|
||||||
|
<link rel="stylesheet" href="/assets/style.css">
|
||||||
|
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header id="site_head">
|
||||||
|
<h1><a href="/">Go SSG</a></h1>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<ul class="list-none">
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/">Home</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/about/">About</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/blog/">Blog</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/contact/">Contact</a></li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<h1>Category: Go</h1>
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<li><a href="/blog/another-blog-post/">Go SSG - Another Blog Post</a><br>A longer summary of the post. Lorem ipsum dolor set amit.</li>
|
||||||
|
|
||||||
|
<li><a href="/blog/my-first-blog-post/">Go SSG - My First Blog Post</a><br>A short summary of the post.</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
<footer id="site_foot">
|
||||||
|
<p class="p-0 py-4 m-0 leading-none">© 2025 Keith Solomon - Go SSG</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
49
public/blog/category/progrmming/index.html
Normal file
49
public/blog/category/progrmming/index.html
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Category: Progrmming</title>
|
||||||
|
<link rel="stylesheet" href="/assets/style.css">
|
||||||
|
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header id="site_head">
|
||||||
|
<h1><a href="/">Go SSG</a></h1>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<ul class="list-none">
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/">Home</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/about/">About</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/blog/">Blog</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/contact/">Contact</a></li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<h1>Category: Progrmming</h1>
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<li><a href="/blog/my-first-blog-post/">Go SSG - My First Blog Post</a><br>A short summary of the post.</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
<footer id="site_foot">
|
||||||
|
<p class="p-0 py-4 m-0 leading-none">© 2025 Keith Solomon - Go SSG</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
54
public/blog/index.html
Normal file
54
public/blog/index.html
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Blog Index</title>
|
||||||
|
<link rel="stylesheet" href="/assets/style.css">
|
||||||
|
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header id="site_head">
|
||||||
|
<h1><a href="/">Go SSG</a></h1>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<ul class="list-none">
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/">Home</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/about/">About</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/blog/">Blog</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/contact/">Contact</a></li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div class="main">
|
||||||
|
<h1>Go SSG - Blog Index</h1>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<li><a href="/blog/another-blog-post/">Go SSG - Another Blog Post</a><br>A longer summary of the post. Lorem ipsum dolor set amit.</li>
|
||||||
|
|
||||||
|
<li><a href="/blog/my-first-blog-post/">Go SSG - My First Blog Post</a><br>A short summary of the post.</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
<footer id="site_foot">
|
||||||
|
<p class="p-0 py-4 m-0 leading-none">© 2025 Keith Solomon - Go SSG</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
75
public/blog/my-first-blog-post/index.html
Normal file
75
public/blog/my-first-blog-post/index.html
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Go SSG - My First Blog Post</title>
|
||||||
|
<link rel="stylesheet" href="/assets/style.css">
|
||||||
|
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header id="site_head">
|
||||||
|
<h1><a href="/">Go SSG</a></h1>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<ul class="list-none">
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/">Home</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/about/">About</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/blog/">Blog</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/contact/">Contact</a></li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<article>
|
||||||
|
<h1>Go SSG - My First Blog Post</h1>
|
||||||
|
<p><small>2025-04-19</small></p>
|
||||||
|
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Categories:
|
||||||
|
|
||||||
|
|
||||||
|
<a href="/blog/category/progrmming/">progrmming</a>
|
||||||
|
|
||||||
|
,
|
||||||
|
<a href="/blog/category/go/">go</a>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<h2><em>Tap Tap Tap</em> Is this thing on?</h2>
|
||||||
|
|
||||||
|
<p class="text-primary">This is my first Go SSG blog post.</p>
|
||||||
|
|
||||||
|
<p>This project is a simple static site generator written in Go. It is designed to be easy to use and extend, allowing you to create a static website quickly and efficiently.</p>
|
||||||
|
|
||||||
|
<p>It supports features like:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Markdown content</li>
|
||||||
|
<li>Customizable templates</li>
|
||||||
|
<li>Simple configuration</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
<footer id="site_foot">
|
||||||
|
<p class="p-0 py-4 m-0 leading-none">© 2025 Keith Solomon - Go SSG</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
79
public/contact/index.html
Normal file
79
public/contact/index.html
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Contact</title>
|
||||||
|
<link rel="stylesheet" href="/assets/style.css">
|
||||||
|
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header id="site_head">
|
||||||
|
<h1><a href="/">Go SSG</a></h1>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<ul class="list-none">
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/">Home</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/about/">About</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/blog/">Blog</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/contact/">Contact</a></li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<article>
|
||||||
|
<h1 class="text-2xl font-bold mb-4">Contact Me</h1>
|
||||||
|
|
||||||
|
<form action="https://api.staticforms.xyz/submit" method="POST" class="space-y-4 max-w-xl">
|
||||||
|
|
||||||
|
<input type="hidden" name="accessKey" value="sf_5m86ek16hmele1jlkl62ghml" />
|
||||||
|
|
||||||
|
|
||||||
|
<input type="hidden" name="redirectTo" value="/thanks/" />
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<span>Name</span>
|
||||||
|
<input id="name" type="text" name="name" required class="w-full border p-2 rounded" />
|
||||||
|
<span class="text-red-600 text-sm hidden" data-error="name"></span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<span>Email</span>
|
||||||
|
<input id="email" type="email" name="email" required class="w-full border p-2 rounded" />
|
||||||
|
<span class="text-red-600 text-sm hidden" data-error="email"></span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<span>Message</span>
|
||||||
|
<textarea id="message" name="message" rows="5" required class="w-full border p-2 rounded"></textarea>
|
||||||
|
<span class="text-red-600 text-sm hidden" data-error="message"></span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<button type="submit" class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700">
|
||||||
|
Send Message
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
<footer id="site_foot">
|
||||||
|
<p class="p-0 py-4 m-0 leading-none">© 2025 Keith Solomon - Go SSG</p>
|
||||||
|
</footer>
|
||||||
|
<script src="/assets/contact.js" defer></script></body>
|
||||||
|
</html>
|
||||||
|
|
||||||
@@ -1,35 +1,48 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Index</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<nav>
|
|
||||||
<ul>
|
|
||||||
|
|
||||||
<li><a href="/about/">About</a></li>
|
|
||||||
|
|
||||||
<li><a href="/">Index</a></li>
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<h1>Welcome
|
|
||||||
|
|
||||||
|
|
||||||
<p>
|
<!DOCTYPE html>
|
||||||
This is your first static site built with Go!
|
<html lang="en">
|
||||||
</p>
|
|
||||||
|
<head>
|
||||||
</main>
|
<meta charset="UTF-8">
|
||||||
|
<title>Go SSG - Home</title>
|
||||||
<footer>
|
<link rel="stylesheet" href="/assets/style.css">
|
||||||
<p>© 2025 Keith Solomon</p>
|
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon">
|
||||||
</footer>
|
</head>
|
||||||
</body>
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header id="site_head">
|
||||||
|
<h1><a href="/">Go SSG</a></h1>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<ul class="list-none">
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/">Home</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/about/">About</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/blog/">Blog</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/contact/">Contact</a></li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<article>
|
||||||
|
<h1>Go SSG - Home</h1>
|
||||||
|
<p>Welcome to my static site built with Go!</p>
|
||||||
|
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
<footer id="site_foot">
|
||||||
|
<p class="p-0 py-4 m-0 leading-none">© 2025 Keith Solomon - Go SSG</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|||||||
47
public/thanks/index.html
Normal file
47
public/thanks/index.html
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Thank You</title>
|
||||||
|
<link rel="stylesheet" href="/assets/style.css">
|
||||||
|
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header id="site_head">
|
||||||
|
<h1><a href="/">Go SSG</a></h1>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<ul class="list-none">
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/">Home</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/about/">About</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/blog/">Blog</a></li>
|
||||||
|
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="/contact/">Contact</a></li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div class="max-w-2xl mx-auto mt-10 text-center">
|
||||||
|
<h1 class="text-3xl font-bold text-green-700 mb-4">Thanks for reaching out!</h1>
|
||||||
|
<p class="text-lg">Your message has been received. I’ll get back to you as soon as I can.</p>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
<footer id="site_foot">
|
||||||
|
<p class="p-0 py-4 m-0 leading-none">© 2025 Keith Solomon - Go SSG</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
40
templates/base.html
Normal file
40
templates/base.html
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
{{ define "base" }}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>{{ .Title }}</title>
|
||||||
|
<link rel="stylesheet" href="/assets/style.css">
|
||||||
|
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{{ template "header" . }}
|
||||||
|
|
||||||
|
<main>
|
||||||
|
{{- if eq .PageTemplate "blog_index" -}}
|
||||||
|
{{ template "blog_index_content" . }}
|
||||||
|
{{- else if eq .PageTemplate "static" -}}
|
||||||
|
{{ template "static_content" . }}
|
||||||
|
{{- else if eq .PageTemplate "blog_post" -}}
|
||||||
|
{{ template "blog_post_content" . }}
|
||||||
|
{{- else if eq .PageTemplate "category_page" -}}
|
||||||
|
{{ template "category_content" . }}
|
||||||
|
{{- else if eq .PageTemplate "contact_page" -}}
|
||||||
|
{{ template "contact_content" . }}
|
||||||
|
{{- else if eq .PageTemplate "thanks_page" -}}
|
||||||
|
{{ template "thanks_content" . }}
|
||||||
|
{{- else -}}
|
||||||
|
<p>Unknown PageTemplate: {{ .PageTemplate }}</p>
|
||||||
|
{{- end -}}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
{{ template "footer" . }}
|
||||||
|
|
||||||
|
{{- if eq .PageTemplate "contact_page" -}}
|
||||||
|
<script src="/assets/contact.js" defer></script>
|
||||||
|
{{- end -}}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{ end }}
|
||||||
17
templates/blog_index_page.html
Normal file
17
templates/blog_index_page.html
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{{ define "blog_index_page" }}
|
||||||
|
{{ template "base" . }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "blog_index_content" }}
|
||||||
|
<div class="main">
|
||||||
|
<h1>Go SSG - Blog Index</h1>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{{ range .Posts }}
|
||||||
|
<li><a href="/blog/{{ .Slug }}/">{{ .Title }}</a><br>{{ .Description }}</li>
|
||||||
|
{{ else }}
|
||||||
|
<li>No posts found.</li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
22
templates/blog_post_page.html
Normal file
22
templates/blog_post_page.html
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{{ define "blog_post_page" }}
|
||||||
|
{{ template "base" . }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "blog_post_content" }}
|
||||||
|
<article>
|
||||||
|
<h1>{{ .Title }}</h1>
|
||||||
|
{{ if .Date }}<p><small>{{ .Date }}</small></p>{{ end }}
|
||||||
|
|
||||||
|
{{ if .Categories }}
|
||||||
|
<p>
|
||||||
|
Categories:
|
||||||
|
{{ range $i, $cat := .Categories }}
|
||||||
|
{{ if $i }}, {{ end }}
|
||||||
|
<a href="/blog/category/{{ $cat | slugify }}/">{{ $cat }}</a>
|
||||||
|
{{ end }}
|
||||||
|
</p>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ .Content }}
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
||||||
12
templates/category_page.html
Normal file
12
templates/category_page.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{{ define "category_page" }}
|
||||||
|
{{ template "base" . }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "category_content" }}
|
||||||
|
<h1>{{ .Title }}</h1>
|
||||||
|
<ul>
|
||||||
|
{{ range .Posts }}
|
||||||
|
<li><a href="/blog/{{ .Slug }}/">{{ .Title }}</a><br>{{ .Description }}</li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
{{ end }}
|
||||||
42
templates/contact_page.html
Normal file
42
templates/contact_page.html
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{{ define "contact_page" }}
|
||||||
|
{{ template "base" . }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "contact_content" }}
|
||||||
|
<article>
|
||||||
|
<h1 class="text-2xl font-bold mb-4">Contact Me</h1>
|
||||||
|
|
||||||
|
<form action="https://api.staticforms.xyz/submit" method="POST" class="space-y-4 max-w-xl">
|
||||||
|
<!-- StaticForms API Key -->
|
||||||
|
<input type="hidden" name="accessKey" value="sf_5m86ek16hmele1jlkl62ghml" />
|
||||||
|
|
||||||
|
<!-- Optional redirect after success -->
|
||||||
|
<input type="hidden" name="redirectTo" value="/thanks/" />
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<span>Name</span>
|
||||||
|
<input id="name" type="text" name="name" required class="w-full border p-2 rounded" />
|
||||||
|
<span class="text-red-600 text-sm hidden" data-error="name"></span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<span>Email</span>
|
||||||
|
<input id="email" type="email" name="email" required class="w-full border p-2 rounded" />
|
||||||
|
<span class="text-red-600 text-sm hidden" data-error="email"></span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<span>Message</span>
|
||||||
|
<textarea id="message" name="message" rows="5" required class="w-full border p-2 rounded"></textarea>
|
||||||
|
<span class="text-red-600 text-sm hidden" data-error="message"></span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<!-- reCAPTCHA if configured in dashboard -->
|
||||||
|
<!-- <div class="g-recaptcha" data-sitekey="your-recaptcha-site-key"></div> -->
|
||||||
|
|
||||||
|
<button type="submit" class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700">
|
||||||
|
Send Message
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
||||||
22
templates/feed.xml
Normal file
22
templates/feed.xml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{{- define "rss_feed" -}}
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<rss version="2.0">
|
||||||
|
<channel>
|
||||||
|
<title>{{ .Title }}</title>
|
||||||
|
<link>{{ .SiteURL }}</link>
|
||||||
|
<description>{{ .Description }}</description>
|
||||||
|
<language>en-us</language>
|
||||||
|
<lastBuildDate>{{ .BuildDate }}</lastBuildDate>
|
||||||
|
|
||||||
|
{{ range .Posts }}
|
||||||
|
<item>
|
||||||
|
<title>{{ .Title }}</title>
|
||||||
|
<link>{{ $.SiteURL }}/blog/{{ .Slug }}/</link>
|
||||||
|
<guid>{{ $.SiteURL }}/blog/{{ .Slug }}/</guid>
|
||||||
|
<pubDate>{{ .DateFormatted }}</pubDate>
|
||||||
|
<description><![CDATA[{{ .Description }}]]></description>
|
||||||
|
</item>
|
||||||
|
{{ end }}
|
||||||
|
</channel>
|
||||||
|
</rss>
|
||||||
|
{{- end -}}
|
||||||
5
templates/footer.html
Normal file
5
templates/footer.html
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{{ define "footer" }}
|
||||||
|
<footer id="site_foot">
|
||||||
|
<p class="p-0 py-4 m-0 leading-none">© {{ .Year }} Keith Solomon - Go SSG</p>
|
||||||
|
</footer>
|
||||||
|
{{ end }}
|
||||||
13
templates/header.html
Normal file
13
templates/header.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{{ define "header" }}
|
||||||
|
<header id="site_head">
|
||||||
|
<h1><a href="/">Go SSG</a></h1>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<ul class="list-none">
|
||||||
|
{{ range .Nav }}
|
||||||
|
<li class="list-none"><a class="text-white hover:text-primary-200" href="{{ .URL }}">{{ .Title }}</a></li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
{{ end }}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>{{ .Title }}</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<nav>
|
|
||||||
<ul>
|
|
||||||
{{ range .Nav }}
|
|
||||||
<li><a href="{{ .URL }}">{{ .Title }}</a></li>
|
|
||||||
{{ end }}
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
{{ .Content }}
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
<p>© {{ .Year }} Keith Solomon</p>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
10
templates/static_page.html
Normal file
10
templates/static_page.html
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{{ define "static_page" }}
|
||||||
|
{{ template "base" . }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "static_content" }}
|
||||||
|
<article>
|
||||||
|
<h1>{{ .Title }}</h1>
|
||||||
|
{{ .Content }}
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
||||||
10
templates/thanks_page.html
Normal file
10
templates/thanks_page.html
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{{ define "thanks_page" }}
|
||||||
|
{{ template "base" . }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "thanks_content" }}
|
||||||
|
<div class="max-w-2xl mx-auto mt-10 text-center">
|
||||||
|
<h1 class="text-3xl font-bold text-green-700 mb-4">Thanks for reaching out!</h1>
|
||||||
|
<p class="text-lg">Your message has been received. I’ll get back to you as soon as I can.</p>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
Reference in New Issue
Block a user