export const GH_ACTION_CONTENT = `# Learn more → https://github.com/TryGhost/action-deploy-theme#getting-started
name: Build and deploy theme
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm ci
- run: npm run build
- name: Deploy theme
uses: TryGhost/action-deploy-theme@v1
with:
api-url: \${{ secrets.GHOST_ADMIN_API_URL }}
api-key: \${{ secrets.GHOST_ADMIN_API_KEY }}`;
export const DEFAULT_TEMPLATE_CONTENT = `
{{meta_title}}
{{!-- Add preconnect for jsdelivr --}}
{{!-- Critical script for dark mode to prevent flash. Replaced during build. --}}
{{ghost_head}}
{{> "header"}}
{{{body}}}
{{> "footer"}}
{{ghost_foot}}
`;
export const POST_TEMPLATE_CONTENT = `{{!< default}}
{{#post}}
{{> "feature-image"}}
{{#if comments}}
{{/if}}
{{/post}}
{{!-- Related posts --}}
{{#if @custom.show_related_articles}}
{{#get "posts" include="authors" filter="id:-{{post.id}}" limit="4" as |related|}}
{{#if related}}
{{t "Read more"}}
{{#foreach related}}
{{> "card"}}
{{/foreach}}
{{/if}}
{{/get}}
{{/if}}`;
export const INDEX_TEMPLATE_CONTENT = `{{!< default}}
{{#foreach posts}}
{{> "card"}}
{{/foreach}}
{{pagination}}
`;
export const PAGE_TEMPLATE_CONTENT = `{{!< default}}
{{#post}}
{{#match @page.show_title_and_feature_image}}
{{title}}
{{> "feature-image"}}
{{/match}}
{{/post}}`;
export const AUTHOR_TEMPLATE_CONTENT = `{{!< default}}
{{#author}}
{{!-- author header --}}
{{/author}}
{{#foreach posts}}
{{> "card"}}
{{/foreach}}
{{pagination}}
`;
export const TAG_TEMPLATE_CONTENT = `{{!< default}}
{{#foreach posts}}
{{> "card"}}
{{/foreach}}
{{pagination}}
`;
export const ERROR_404_TEMPLATE_CONTENT = `{{!< default}}
404
Page not found
Sorry, we couldn't find the page you're looking for. It might have been moved or deleted.
Back to Home
`;
export const CARD_PARTIAL_CONTENT = `
{{#if feature_image}}
{{/if}}
{{#primary_tag}}
{{name}}
{{/primary_tag}}
{{title}}
{{excerpt words="30"}}
`;
export const HEADER_PARTIAL_CONTENT = ``;
export const FOOTER_PARTIAL_CONTENT = ``;
export const NAVIGATION_PARTIAL_CONTENT = `
{{#foreach navigation}}
-
{{label}}
{{/foreach}}
`;
export const GHOST_CSS_CONTENT = `/* Ghost image styles */
.kg-width-wide img { max-width: 80vw; width: 100%; margin: 2rem auto; }
.kg-width-full img { max-width: 100vw; width: 100%; margin: 2rem auto; }
.kg-card figcaption { text-align: center; font-size: 0.8rem; margin-top: 0.5rem; opacity: 0.6; }`;
export const INDEX_CSS_CONTENT = `@import "ghost.css";
`;
export const CRITICAL_CSS_CONTENT = `@import "reset.css";
`;
export const CSS_RESET = `
/* https://www.joshwcomeau.com/css/custom-css-reset/ */
/* 1. Use a more-intuitive box-sizing model */
*, *::before, *::after {
box-sizing: border-box;
}
/* 2. Remove default margin */
*:not(dialog) {
margin: 0;
}
/* 3. Enable keyword animations */
@media (prefers-reduced-motion: no-preference) {
html {
interpolate-size: allow-keywords;
}
}
body {
/* 4. Increase line-height */
line-height: 1.5;
/* 5. Improve text rendering */
-webkit-font-smoothing: antialiased;
}
/* 6. Improve media defaults */
img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}
/* 7. Inherit fonts for form controls */
input, button, textarea, select {
font: inherit;
}
/* 8. Avoid text overflows */
p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}
/* 9. Improve line wrapping */
p {
text-wrap: pretty;
}
h1, h2, h3, h4, h5, h6 {
text-wrap: balance;
}
`;
export const PACKAGE_JSON_TEMPLATE = (name: string) => `{
"name": "${name}",
"description": "A new Ghost theme",
"version": "0.1.0",
"engines": {
"ghost": ">=6.0.0"
},
"author": {
"name": "Example Name",
"email": "name@example.com",
"url": "https://example.com"
},
"license": "MIT",
"keywords": [
"ghost-theme",
"Ghost",
"Theme"
],
"config": {
"posts_per_page": 25,
"image_sizes": {
"50": {
"width": 50
},
"100": {
"width": 100
},
"400": {
"width": 400
},
"600": {
"width": 600
},
"800": {
"width": 800
},
"1000": {
"width": 1000
},
"1200": {
"width": 1200
},
"1600": {
"width": 1600
},
"2000": {
"width": 2000
}
},
"custom": {
"show_related_articles": {
"type": "boolean",
"default": true
}
}
},
"scripts": {
"dev": "gtb dev",
"build": "gtb",
"lint": "gtb lint",
"check": "gtb check",
"zip": "gtb zip"
},
"devDependencies": {
"@royalfig/gtb": "latest",
"eslint": "^10.7.0",
"@eslint/js": "^10.0.1",
"globals": "^17.7.0",
"@types/eslint__js": "^9.14.0",
"@types/node": "^26.1.1",
"typescript": "^7.0.2",
"typescript-eslint": "^8.63.0",
"stylelint": "^17.14.0",
"stylelint-config-standard": "^40.0.0",
"stylelint-config-recess-order": "^7.7.0"
},
"type": "module"
}
`;
export const ESLINT_CONFIG_TEMPLATE = `{
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/stylistic",
],
rules: {
"no-console": 1,
},
}
`;
export const STYLELINT_CONFIG_TEMPLATE = `{
"extends": ["stylelint-config-standard", "stylelint-config-recess-order"],
"rules": {
"import-notation": null,
"hue-degree-notation": "number"
}
}`;
export const DARK_MODE_CRITICAL_JS = `function toggleDarkModeShareButton(newMode: string) {
const shareButton = document.querySelector("share-button");
if (!shareButton) {
return;
}
const modeAsBool = newMode === "dark" ? "true" : "false";
shareButton?.setAttribute("dark-mode", modeAsBool);
}
function autoDarkMode() {
if (
window.matchMedia &&
window.matchMedia("(prefers-color-scheme: dark)").matches
) {
document.documentElement.dataset.mode = "dark";
} else {
document.documentElement.dataset.mode = "light";
}
}
function setDarkMode() {
const prefers = localStorage.getItem("s-dark-mode");
if (prefers) {
document.documentElement.dataset.mode = prefers;
} else {
autoDarkMode();
}
}
window
.matchMedia("(prefers-color-scheme: dark)")
.addEventListener("change", (e) => {
const prefers = localStorage.getItem("s-dark-mode");
// Abort if user has already set a preference
if (prefers) {
return;
}
if (e.matches) {
toggleDarkModeShareButton("dark");
} else {
toggleDarkModeShareButton("light");
}
autoDarkMode();
});
setDarkMode();`;
export const DARK_MODE_HANDLER_JS = `export function darkModeHandler() {
const darkModeToggleButton = document.querySelectorAll(".s-dark-mode-toggle");
if (!darkModeToggleButton) {
return;
}
const mode = document.documentElement.dataset.mode!;
toggleDarkModeShareButton(mode);
Array.from(darkModeToggleButton).map((btn) =>
btn.addEventListener("click", () => {
const newMode = invertMode();
toggleDarkModeShareButton(newMode);
document.documentElement.dataset.mode = newMode;
localStorage.setItem("s-dark-mode", newMode);
})
);
}
export function toggleDarkModeShareButton(newMode: string) {
const shareButton = document.querySelector("share-button");
if (!shareButton) {
return;
}
const modeAsBool = newMode === "dark" ? "true" : "false";
shareButton?.setAttribute("dark-mode", modeAsBool);
}
export function invertMode() {
const currentMode = document.documentElement.dataset.mode!;
return currentMode === "dark" ? "light" : "dark";
}`;
export const DARK_MODE_TOGGLE_HBS = ``;
export const EXTERNAL_ASSETS = [
"*.png",
"*.jpg",
"*.jpeg",
"*.svg",
"*.gif",
"*.woff",
"*.woff2",
"*.ttf",
"*.otf",
] as const;
export const FEATURE_IMAGE_PARTIAL_CONTENT = `{{#if feature_image}}
{{#if feature_image_caption}}
{{feature_image_caption}}
{{/if}}
{{/if}}`;
export const LOCALES_EN_JSON_CONTENT = `{
"Search": "Search",
"Subscribe": "Subscribe",
"Account": "Account",
"Read more": "Read more"
}`;
export const GITIGNORE_CONTENT = `node_modules
assets/built
.DS_Store
.env`;