new template test
This commit is contained in:
parent
3fb13d14c5
commit
c95af3948d
|
@ -6,4 +6,4 @@ export interface Context {
|
||||||
args: string[];
|
args: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Template = "basic";
|
export type Template = ["basic","starterkit"];
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@matthiesenxyz/create-astro-ghostcms",
|
"name": "@matthiesenxyz/create-astro-ghostcms",
|
||||||
"version": "0.0.1-dev25",
|
"version": "0.0.1-dev26",
|
||||||
"description": "Utility to quickly get started with our Integration and astro",
|
"description": "Utility to quickly get started with our Integration and astro",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./create-astro-ghostcms.mjs",
|
"main": "./create-astro-ghostcms.mjs",
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import arg from "arg";
|
import arg from "arg";
|
||||||
import * as p from "@clack/prompts";
|
import * as p from "@clack/prompts";
|
||||||
|
import c from 'picocolors';
|
||||||
import { exitPrompt, isPackageManager } from "./lib/utils.js";
|
import { exitPrompt, isPackageManager } from "./lib/utils.js";
|
||||||
import { createBasic } from "./runners/basic.js";
|
import { createBasic } from "./runners/basic.js";
|
||||||
import c from 'picocolors';
|
import { createStarterKit } from "./runners/starterkit.js";
|
||||||
|
|
||||||
|
|
||||||
export async function main() {
|
export async function main() {
|
||||||
|
@ -63,6 +64,10 @@ export async function main() {
|
||||||
value: "basic",
|
value: "basic",
|
||||||
label: `${c.magenta('Basic')} - ${c.cyan(c.italic('Integration w/ Default Theme'))}`
|
label: `${c.magenta('Basic')} - ${c.cyan(c.italic('Integration w/ Default Theme'))}`
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: "starterkit",
|
||||||
|
label: `${c.magenta('Starter Kit')} - ${c.cyan(c.italic('Integration in API-Only Mode with customizable theme'))}`
|
||||||
|
}
|
||||||
],
|
],
|
||||||
initialValue: "basic",
|
initialValue: "basic",
|
||||||
});
|
});
|
||||||
|
@ -85,6 +90,9 @@ export async function main() {
|
||||||
case "basic":
|
case "basic":
|
||||||
await createBasic(ctx).catch(console.error);
|
await createBasic(ctx).catch(console.error);
|
||||||
break;
|
break;
|
||||||
|
case "starterkit":
|
||||||
|
await createStarterKit(ctx).catch(console.error);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(c.red(`Unknown template: ${template}`));
|
throw new Error(c.red(`Unknown template: ${template}`));
|
||||||
}
|
}
|
||||||
|
@ -102,7 +110,7 @@ function getHelp() {
|
||||||
* @returns {template is Template}
|
* @returns {template is Template}
|
||||||
*/
|
*/
|
||||||
function isValidTemplate(template) {
|
function isValidTemplate(template) {
|
||||||
return ["basic"].includes(template);
|
return ["basic","starterkit"].includes(template);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import fse from "fs-extra";
|
import fse from "fs-extra";
|
||||||
import c from 'picocolors';
|
import c from 'picocolors';
|
||||||
import { execa } from "execa";
|
|
||||||
import * as p from "@clack/prompts";
|
import * as p from "@clack/prompts";
|
||||||
|
import { execa } from "execa";
|
||||||
import { exitPrompt, getModulePaths, isPathname,
|
import { exitPrompt, getModulePaths, isPathname,
|
||||||
normalizePath, wait } from "../lib/utils.js";
|
normalizePath, wait } from "../lib/utils.js";
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,245 @@
|
||||||
|
import path from "node:path";
|
||||||
|
import fse from "fs-extra";
|
||||||
|
import c from 'picocolors';
|
||||||
|
import * as p from "@clack/prompts";
|
||||||
|
import { execa } from "execa";
|
||||||
|
import { exitPrompt, getModulePaths, isPathname,
|
||||||
|
normalizePath, wait } from "../lib/utils.js";
|
||||||
|
|
||||||
|
const runnerName = "starterkit";
|
||||||
|
|
||||||
|
/** @param {Context} ctx */
|
||||||
|
export async function createStarterKit(ctx) {
|
||||||
|
let { args, dryRun, initGitRepo, installDeps } = ctx;
|
||||||
|
|
||||||
|
const s = p.spinner();
|
||||||
|
let cwd = process.cwd();
|
||||||
|
|
||||||
|
// 1. Set up project directory
|
||||||
|
const project = await getProjectDetails(args[0], { cwd });
|
||||||
|
if (dryRun) {
|
||||||
|
await wait(1);
|
||||||
|
} else {
|
||||||
|
await fse.ensureDir(project.pathname);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Create the damned thing
|
||||||
|
cwd = project.pathname;
|
||||||
|
const relativePath = path.relative(process.cwd(), project.pathname);
|
||||||
|
s.start(`${c.yellow(`Creating a new Astro-GhostCMS project in ${relativePath}`)}`);
|
||||||
|
if (dryRun) {
|
||||||
|
await wait(2000);
|
||||||
|
} else {
|
||||||
|
await createApp(project.name, project.pathname, {
|
||||||
|
onError(error) {
|
||||||
|
s.stop(`${c.red('Failed to create new project')}`);
|
||||||
|
p.cancel();
|
||||||
|
console.error(error);
|
||||||
|
process.exit(1);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
s.stop(`${c.green('New Astro-GhostCMS project')} '${project.name}' ${c.green('created')} 🚀`);
|
||||||
|
const fCheck = await p.group({
|
||||||
|
installDeps: () => p.confirm({
|
||||||
|
message: `${c.cyan('Install dependencies? (Recommended)')}`,
|
||||||
|
initialValue: false,
|
||||||
|
}),
|
||||||
|
//GitRepo: () => p.confirm({
|
||||||
|
// message: `${c.cyan('Initialize a Git repository?')} ${c.italic(c.gray("( Tip: This Option gets Stuck Press Enter Twice if you get no reponse! )"))}`,
|
||||||
|
// initialValue: false,
|
||||||
|
//}),
|
||||||
|
readyCheck: () => p.confirm({
|
||||||
|
message: `${c.bgYellow(c.black(c.bold(' CONFIRM: Press Enter Twice to continue or `Ctrl+C` to Cancel. ')))}`,
|
||||||
|
initialValue: true,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{ onCancel: () => { exitPrompt(); } });
|
||||||
|
|
||||||
|
if(fCheck.readyCheck){;
|
||||||
|
// 3. Initialize git repo
|
||||||
|
if (initGitRepo) {
|
||||||
|
if (dryRun) {
|
||||||
|
await wait(1);
|
||||||
|
} else {
|
||||||
|
await exec("git", ["init"], { cwd });
|
||||||
|
}
|
||||||
|
p.log.success(c.green("Initialized Git repository"));
|
||||||
|
} else {
|
||||||
|
p.log.info(`${c.gray("Skipped Git initialization")}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextSteps = `If you didnt opt to install Dependencies dont forget to run: \n ${c.yellow('npm install')} / ${c.yellow('pnpm install')} / ${c.yellow('yarn install')} inside your project directory! \n \n ${c.bgYellow(c.black(c.bold(" Dont forget to modify your .env file for YOUR ghost install! ")))} `
|
||||||
|
|
||||||
|
// 4. Install dependencies
|
||||||
|
installDeps = installDeps ?? fCheck.installDeps;
|
||||||
|
const pm = ctx.pkgManager ?? "pnpm";
|
||||||
|
if (installDeps) {
|
||||||
|
s.start(`${c.cyan(`Installing dependencies with ${pm}`)} `);
|
||||||
|
if (dryRun) {
|
||||||
|
await wait(1);
|
||||||
|
} else {
|
||||||
|
await installDependencies(pm, { cwd });
|
||||||
|
}
|
||||||
|
s.stop(`${c.green(`Dependencies installed with ${pm}`)}`);
|
||||||
|
success()
|
||||||
|
} else {
|
||||||
|
p.log.info(`${c.gray('Skipped dependency installation')}`);
|
||||||
|
success()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function success() {
|
||||||
|
p.note(nextSteps);
|
||||||
|
p.outro(c.green("Deployment Complete!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
exitPrompt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} projectName
|
||||||
|
* @param {string} projectPathname
|
||||||
|
* @param {{ onError: (err: unknown) => any }} opts
|
||||||
|
*/
|
||||||
|
async function createApp(projectName, projectPathname, { onError }) {
|
||||||
|
const { pathname } = getModulePaths(import.meta.url);
|
||||||
|
const templatesDir = path.resolve(pathname, "..", "..", "templates");
|
||||||
|
const sharedTemplateDir = path.join(templatesDir, "_shared");
|
||||||
|
const runnerTemplateDir = path.join(templatesDir, runnerName);
|
||||||
|
|
||||||
|
await fse.ensureDir(projectPathname);
|
||||||
|
|
||||||
|
// TODO: Detect if project directory is empty, otherwise we
|
||||||
|
// can't create a new project here.
|
||||||
|
await fse.copy(runnerTemplateDir, projectPathname);
|
||||||
|
|
||||||
|
// Copy misc files from shared
|
||||||
|
const filesToCopy = [
|
||||||
|
{
|
||||||
|
src: path.join(sharedTemplateDir, ".env"),
|
||||||
|
dest: path.join(projectPathname, ".env"),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
await Promise.all(
|
||||||
|
filesToCopy.map(async ({ src, dest }) => await fse.copy(src, dest))
|
||||||
|
);
|
||||||
|
|
||||||
|
/** @type {Array<{ pathname: string; getUpdates: (contents: string) => string }>} */
|
||||||
|
const filesToUpdate = [
|
||||||
|
{
|
||||||
|
pathname: path.join(projectPathname, "package.json"),
|
||||||
|
getUpdates: updateProjectName,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
await Promise.all(
|
||||||
|
filesToUpdate.map(async ({ pathname, getUpdates }) => {
|
||||||
|
const contents = await fse.readFile(pathname, "utf-8");
|
||||||
|
const updatedContents = getUpdates(contents);
|
||||||
|
await fse.writeFile(pathname, updatedContents, "utf-8");
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/** @param {string} contents */
|
||||||
|
function updateProjectName(contents) {
|
||||||
|
return contents.replace(/{{PROJECT_NAME}}/g, projectName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string|undefined} projectNameInput
|
||||||
|
* @param {{ cwd: string }} opts
|
||||||
|
*/
|
||||||
|
async function getProjectDetails(projectNameInput, opts) {
|
||||||
|
let projectName = projectNameInput;
|
||||||
|
if (!projectName) {
|
||||||
|
const defaultProjectName = "my-astro-ghost";
|
||||||
|
let answer = await p.text({
|
||||||
|
message: `${c.cyan("Where would you like to create your project?")}`,
|
||||||
|
placeholder: `.${path.sep}${defaultProjectName}`,
|
||||||
|
});
|
||||||
|
if (p.isCancel(answer)) exitPrompt();
|
||||||
|
|
||||||
|
answer = answer?.trim();
|
||||||
|
projectName = answer || defaultProjectName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
let pathname;
|
||||||
|
|
||||||
|
if (isPathname(projectName)) {
|
||||||
|
const dir = path.resolve(opts.cwd, path.dirname(normalizePath(projectName)));
|
||||||
|
projectName = toValidProjectName(path.basename(projectName));
|
||||||
|
pathname = path.join(dir, projectName);
|
||||||
|
} else {
|
||||||
|
projectName = toValidProjectName(projectName);
|
||||||
|
pathname = path.resolve(opts.cwd, projectName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: projectName,
|
||||||
|
pathname,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} command
|
||||||
|
* @param {readonly string[]} args
|
||||||
|
* @param {{ cwd: string }} options
|
||||||
|
* @returns {Promise<{ code: number | null; signal: NodeJS.Signals | null }>}
|
||||||
|
*/
|
||||||
|
async function exec(command, args, options) {
|
||||||
|
const installExec = execa(command, ["init"], { ...options, stdio: "ignore" });
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
installExec.on("error", (error) => reject(error));
|
||||||
|
installExec.on("close", (code, signal) => resolve({ code, signal }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {"npm" | "yarn" | "pnpm"} packageManager
|
||||||
|
* @param {{ cwd: string }} opts
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async function installDependencies(packageManager, { cwd }) {
|
||||||
|
const installExec = execa(packageManager, ["install"], { cwd });
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
installExec.on("error", (error) => reject(error));
|
||||||
|
installExec.on("close", () => resolve());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} projectName
|
||||||
|
*/
|
||||||
|
function toValidProjectName(projectName) {
|
||||||
|
if (isValidProjectName(projectName)) {
|
||||||
|
return projectName;
|
||||||
|
}
|
||||||
|
return projectName
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s+/g, "-")
|
||||||
|
.replace(/^[._]/, "")
|
||||||
|
.replace(/[^a-z\d\-~]+/g, "-")
|
||||||
|
.replace(/^-+/, "")
|
||||||
|
.replace(/-+$/, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} projectName
|
||||||
|
*/
|
||||||
|
function isValidProjectName(projectName) {
|
||||||
|
return /^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(
|
||||||
|
projectName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import("../../types.js").Template} Template
|
||||||
|
* @typedef {import("../../types.js").PackageManager} PackageManager
|
||||||
|
* @typedef {import("../../types.js").Context} Context
|
||||||
|
* @typedef {import("../../types.js").Serializable} Serializable
|
||||||
|
*/
|
|
@ -0,0 +1,21 @@
|
||||||
|
# build output
|
||||||
|
dist/
|
||||||
|
# generated types
|
||||||
|
.astro/
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# logs
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
|
||||||
|
# environment variables
|
||||||
|
.env
|
||||||
|
.env.production
|
||||||
|
|
||||||
|
# macOS-specific files
|
||||||
|
.DS_Store
|
4
packages/create-astro-ghostcms/src/templates/starterkit/.vscode/extensions.json
vendored
Normal file
4
packages/create-astro-ghostcms/src/templates/starterkit/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"recommendations": ["astro-build.astro-vscode"],
|
||||||
|
"unwantedRecommendations": []
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"command": "./node_modules/.bin/astro dev",
|
||||||
|
"name": "Development server",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "node-terminal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Adam Matthiesen
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Astro Starter Kit: Astro-GhostCMS Integration
|
||||||
|
|
||||||
|
To get started fork or clone this repo, and use one of the following commands:
|
||||||
|
|
||||||
|
```
|
||||||
|
npm install
|
||||||
|
|
||||||
|
pnpm install
|
||||||
|
|
||||||
|
yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
Then make sure you set your .env variables for DEV and environment variables on your server.
|
||||||
|
|
||||||
|
Its just astro! At this point you are free to start modifying what content/what your website will do, want to make it blog features only? go ahead!
|
|
@ -0,0 +1,17 @@
|
||||||
|
import GhostCMS from '@matthiesenxyz/astro-ghostcms';
|
||||||
|
import { defineConfig } from 'astro/config';
|
||||||
|
|
||||||
|
// https://astro.build/config
|
||||||
|
export default defineConfig({
|
||||||
|
// CHANGE THIS TO MATCH YOUR EXTERNAL DOMAIN
|
||||||
|
site: "http://localhost:4321",
|
||||||
|
integrations: [
|
||||||
|
// Includes GhostCMS API, @astrojs/rss, @astrojs/sitemap, and astro-robots-txt
|
||||||
|
GhostCMS({
|
||||||
|
// This Option Disables all default theme injection and allows DIY mode.
|
||||||
|
disableRouteInjection: true,
|
||||||
|
// Enable this to disable the extra console logs
|
||||||
|
disableConsoleOutput: false
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"name": "{{PROJECT_NAME}}",
|
||||||
|
"type": "module",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "astro dev",
|
||||||
|
"start": "astro dev",
|
||||||
|
"build": "astro check && astro build",
|
||||||
|
"preview": "astro preview",
|
||||||
|
"astro": "astro"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@astrojs/check": "^0.4.1",
|
||||||
|
"@matthiesenxyz/astro-ghostcms": "^3.1.4",
|
||||||
|
"astro": "^4.2.4",
|
||||||
|
"typescript": "^5.3.3",
|
||||||
|
"astro-font": "^0.0.77"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"sass": "^1.70.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
|
||||||
|
<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" />
|
||||||
|
<style>
|
||||||
|
path { fill: #000; }
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
path { fill: #FFF; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 749 B |
|
@ -0,0 +1,80 @@
|
||||||
|
---
|
||||||
|
import { getGhostImgPath } from "../utils";
|
||||||
|
import type { Settings, Author } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
export type Props = {
|
||||||
|
author: Author;
|
||||||
|
wide?: boolean;
|
||||||
|
addClass?: string;
|
||||||
|
settings: Settings;
|
||||||
|
showCover?: boolean;
|
||||||
|
};
|
||||||
|
const {
|
||||||
|
author,
|
||||||
|
wide = false,
|
||||||
|
settings,
|
||||||
|
showCover = true,
|
||||||
|
} = Astro.props as Props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class={`card author-card author-${author.slug} ${wide ? "wide" : ""}`}>
|
||||||
|
<figure class="author-card-cover">
|
||||||
|
{author.cover_image && showCover && (
|
||||||
|
<img
|
||||||
|
class="lazyautosizes lazyloaded"
|
||||||
|
data-srcset={`
|
||||||
|
${getGhostImgPath(settings.url, author.cover_image, 300)} 300w,
|
||||||
|
${getGhostImgPath(settings.url, author.cover_image, 600)} 600w
|
||||||
|
`}
|
||||||
|
srcset={`
|
||||||
|
${getGhostImgPath(settings.url, author.cover_image, 300)} 300w,
|
||||||
|
${getGhostImgPath(settings.url, author.cover_image, 600)} 600w
|
||||||
|
`}
|
||||||
|
data-sizes="auto"
|
||||||
|
data-src={getGhostImgPath(settings.url, author.cover_image, 300)}
|
||||||
|
src={getGhostImgPath(settings.url, author.cover_image, 300)}
|
||||||
|
alt={author.name}
|
||||||
|
sizes="316px"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
{author.profile_image && (
|
||||||
|
<a href={`/author/${author.slug}`} class="author-card-media">
|
||||||
|
<img
|
||||||
|
class="author-card-img"
|
||||||
|
data-srcset={`
|
||||||
|
${getGhostImgPath(settings.url, author.profile_image, 100)} 100w,
|
||||||
|
${getGhostImgPath(settings.url, author.profile_image, 300)} 300w
|
||||||
|
`}
|
||||||
|
srcset={`
|
||||||
|
${getGhostImgPath(settings.url, author.profile_image, 100)} 100w,
|
||||||
|
${getGhostImgPath(settings.url, author.profile_image, 300)} 300w
|
||||||
|
`}
|
||||||
|
data-sizes="auto"
|
||||||
|
data-src={getGhostImgPath(settings.url, author.profile_image, 300)}
|
||||||
|
src={getGhostImgPath(settings.url, author.profile_image, 300)}
|
||||||
|
alt={author.name}
|
||||||
|
sizes="316px"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
<div class="author-card-content">
|
||||||
|
<div class="author-card-name">
|
||||||
|
<a href={`/author/${author.slug}`}>{author.name}</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{author.bio && <div class="author-card-descr">{author.bio}</div>}
|
||||||
|
|
||||||
|
<div class="author-card-details">
|
||||||
|
{author.count && author.count.posts && (
|
||||||
|
<div>
|
||||||
|
{author.count.posts > 0 ? `${author.count.posts} posts` : "No posts"}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,76 @@
|
||||||
|
---
|
||||||
|
import { getGhostImgPath } from "../utils";
|
||||||
|
import type { Settings, Post } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
export type Props = {
|
||||||
|
post: Post;
|
||||||
|
settings: Settings;
|
||||||
|
};
|
||||||
|
const { post, settings } = Astro.props as Props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<ul class="author-list">
|
||||||
|
{post.authors && post.authors.map((author) => (
|
||||||
|
<li class="author-list-item">
|
||||||
|
{author.profile_image ? (
|
||||||
|
<a href={`/author/${author.slug}`} class="author-avatar">
|
||||||
|
<img
|
||||||
|
class="author-profile-image"
|
||||||
|
src={getGhostImgPath(settings.url, author.profile_image, 100)}
|
||||||
|
alt={author.name}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<a href={`/author/${author.slug}`} class="author-avatar">
|
||||||
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
<path
|
||||||
|
d="M3.513 18.998C4.749 15.504 8.082 13 12 13s7.251 2.504 8.487 5.998C18.47 21.442 15.417 23 12 23s-6.47-1.558-8.487-4.002zM12 12c2.21 0 4-2.79 4-5s-1.79-4-4-4-4 1.79-4 4 1.79 5 4 5z"
|
||||||
|
fill="#FFF"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@use "sass:color";
|
||||||
|
@import "../styles/variables";
|
||||||
|
.author-avatar {
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 0 -4px;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border: #fff 2px solid;
|
||||||
|
border-radius: 100%;
|
||||||
|
transition: all 0.5s cubic-bezier(0.4, 0.01, 0.165, 0.99) 700ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin: 0 0 0 4px;
|
||||||
|
padding: 0;
|
||||||
|
padding-inline-end: 1rem;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author-list-item {
|
||||||
|
position: relative;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author-profile-image {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: color.scale($color-lightgrey, $lightness: +10%);
|
||||||
|
border-radius: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,86 @@
|
||||||
|
---
|
||||||
|
import type { Settings } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
import { ViewTransitions } from 'astro:transitions';
|
||||||
|
import { AstroFont } from "astro-font";
|
||||||
|
|
||||||
|
export type Props = {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
permalink?: string;
|
||||||
|
image?: string;
|
||||||
|
settings: Settings;
|
||||||
|
};
|
||||||
|
|
||||||
|
const { description, permalink, image, settings, title } = Astro.props as Props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<AstroFont
|
||||||
|
config={[
|
||||||
|
{
|
||||||
|
src: [],
|
||||||
|
name: "Inter",
|
||||||
|
preload: false,
|
||||||
|
display: "swap",
|
||||||
|
selector: "html",
|
||||||
|
fallback: "sans-serif",
|
||||||
|
googleFontsURL: 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<title>{title}</title>
|
||||||
|
<ViewTransitions />
|
||||||
|
<meta name="title" content={title} />
|
||||||
|
{description && <meta name="description" content={description} />}
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href={settings.icon} />
|
||||||
|
<link rel="shortcut icon" type="image/png" sizes="16x16" href={settings.icon} />
|
||||||
|
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
||||||
|
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="msapplication-TileColor" content="#da532c" />
|
||||||
|
<meta name="msapplication-config" content="/browserconfig.xml" />
|
||||||
|
<meta name="theme-color" content="#ffffff" />
|
||||||
|
|
||||||
|
<!-- Open Graph Tags (Facebook) -->
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:title" content={title} />
|
||||||
|
{permalink && <meta property="og:url" content={permalink} />}
|
||||||
|
{description && <meta property="og:description" content={description} />}
|
||||||
|
{image && <meta property="og:image" content={image} />}
|
||||||
|
|
||||||
|
<!-- Twitter -->
|
||||||
|
<meta property="twitter:card" content="summary_large_image" />
|
||||||
|
<meta property="twitter:title" content={title} />
|
||||||
|
{permalink && <meta property="twitter:url" content={permalink} />}
|
||||||
|
{description && <meta property="twitter:description" content={description} />}
|
||||||
|
{image && <meta property="twitter:image" content={image} />}
|
||||||
|
|
||||||
|
<!-- Link to the global style, or the file that imports constructs -->
|
||||||
|
<link
|
||||||
|
rel="preload"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/themes/prism.min.css"
|
||||||
|
as="style"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="preload"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/prism.min.js"
|
||||||
|
as="script"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="preload stylesheet"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/themes/prism.min.css"
|
||||||
|
as="style"
|
||||||
|
onload="this.onload=null;this.rel='stylesheet'"
|
||||||
|
crossorigin
|
||||||
|
/>
|
||||||
|
<script
|
||||||
|
async
|
||||||
|
defer
|
||||||
|
src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/prism.min.js"
|
||||||
|
>
|
||||||
|
|
||||||
|
</script>
|
|
@ -0,0 +1,32 @@
|
||||||
|
---
|
||||||
|
import { getGhostImgPath } from "../utils";
|
||||||
|
import type { Settings } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
export type Props = {
|
||||||
|
image: string;
|
||||||
|
alt?: string;
|
||||||
|
caption?: string;
|
||||||
|
settings: Settings;
|
||||||
|
transitionName?: string;
|
||||||
|
};
|
||||||
|
const { image, alt, caption = "", settings, transitionName } = Astro.props as Props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<figure class="article-image">
|
||||||
|
<img
|
||||||
|
srcset={`
|
||||||
|
${getGhostImgPath(settings.url, image, 300)} 300w,
|
||||||
|
${getGhostImgPath(settings.url, image, 600)} 600w,
|
||||||
|
${getGhostImgPath(settings.url, image, 1000)} 1000w,
|
||||||
|
${getGhostImgPath(settings.url, image, 2000)} 2000w
|
||||||
|
`}
|
||||||
|
sizes="(min-width: 1400px) 1400px, 92vw"
|
||||||
|
src={getGhostImgPath(settings.url, image, 2000)}
|
||||||
|
alt={alt}
|
||||||
|
transition:name={transitionName}
|
||||||
|
/>
|
||||||
|
{caption && <figcaption><Fragment set:html={caption}></figcaption>}
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,112 @@
|
||||||
|
---
|
||||||
|
import type { Settings } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
export type Props = {
|
||||||
|
settings: Settings;
|
||||||
|
};
|
||||||
|
const { settings } = Astro.props as Props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<footer class="site-footer outer">
|
||||||
|
<div class="inner">
|
||||||
|
<section class="copyright">
|
||||||
|
<a href="/">{settings.title}</a> © 2021
|
||||||
|
</section>
|
||||||
|
<nav class="site-footer-nav">
|
||||||
|
{settings.secondary_navigation.length > 0 && (
|
||||||
|
<ul class="nav secondary">
|
||||||
|
{settings.secondary_navigation.map(({ label, url }) => (
|
||||||
|
<li class="">
|
||||||
|
<a href={url}>{label}</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</nav>
|
||||||
|
<div>
|
||||||
|
<a href="https://ghost.org/" target="_blank" rel="noopener"
|
||||||
|
>Powered by Ghost
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.site-footer {
|
||||||
|
position: relative;
|
||||||
|
margin: 40px 0 0 0;
|
||||||
|
padding: 40px 4vmin 140px;
|
||||||
|
color: #fff;
|
||||||
|
background: var(--color-darkgrey);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer .inner {
|
||||||
|
display: grid;
|
||||||
|
grid-gap: 40px;
|
||||||
|
grid-template-columns: auto 1fr auto;
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
font-size: 1.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer .copyright a {
|
||||||
|
color: #fff;
|
||||||
|
letter-spacing: -0.015em;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer a {
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer a:hover {
|
||||||
|
color: rgba(255, 255, 255, 1);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer-nav ul {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin: 0 0 20px;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer-nav li {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer-nav a {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer-nav li:not(:first-child) a:before {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
width: 2px;
|
||||||
|
height: 2px;
|
||||||
|
margin: 0 10px 0 0;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
.site-footer .inner {
|
||||||
|
max-width: 500px;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
grid-gap: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.site-footer .copyright,
|
||||||
|
.site-footer .copyright a {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,438 @@
|
||||||
|
---
|
||||||
|
import { twitter, facebook } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
import type { Settings } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
export type Props = {
|
||||||
|
settings: Settings;
|
||||||
|
};
|
||||||
|
const { settings } = Astro.props as Props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<header
|
||||||
|
id="gh-head"
|
||||||
|
class={`gh-head has-cover js-header`}
|
||||||
|
>
|
||||||
|
<nav class="gh-head-inner inner gh-container">
|
||||||
|
<div class="gh-head-brand">
|
||||||
|
<a class="gh-head-logo" href="/">
|
||||||
|
{settings.logo && <img src={settings.logo} alt={settings.title} />}
|
||||||
|
{!settings.logo && settings.title}
|
||||||
|
</a>
|
||||||
|
<a class="gh-burger" role="button">
|
||||||
|
<div class="gh-burger-box">
|
||||||
|
<div class="gh-burger-inner"></div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="gh-head-menu">
|
||||||
|
<ul class="nav">
|
||||||
|
{settings.navigation.map(({ label, url }) => (
|
||||||
|
<li class="nav__item">
|
||||||
|
<a href={url}>{label}</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="gh-head-actions">
|
||||||
|
<div class="gh-social">
|
||||||
|
{settings.facebook && (
|
||||||
|
<a
|
||||||
|
class="gh-social-facebook"
|
||||||
|
href={facebook(settings.facebook)}
|
||||||
|
title="Facebook"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>
|
||||||
|
<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M16 0c8.837 0 16 7.163 16 16s-7.163 16-16 16S0 24.837 0 16 7.163 0 16 0zm5.204 4.911h-3.546c-2.103 0-4.443.885-4.443 3.934.01 1.062 0 2.08 0 3.225h-2.433v3.872h2.509v11.147h4.61v-11.22h3.042l.275-3.81h-3.397s.007-1.695 0-2.187c0-1.205 1.253-1.136 1.329-1.136h2.054V4.911z" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{settings.twitter && (
|
||||||
|
<a
|
||||||
|
class="gh-social-twitter"
|
||||||
|
href={twitter(settings.twitter)}
|
||||||
|
title="Twitter"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||||
|
<path d="M30.063 7.313c-.813 1.125-1.75 2.125-2.875 2.938v.75c0 1.563-.188 3.125-.688 4.625a15.088 15.088 0 0 1-2.063 4.438c-.875 1.438-2 2.688-3.25 3.813a15.015 15.015 0 0 1-4.625 2.563c-1.813.688-3.75 1-5.75 1-3.25 0-6.188-.875-8.875-2.625.438.063.875.125 1.375.125 2.688 0 5.063-.875 7.188-2.5-1.25 0-2.375-.375-3.375-1.125s-1.688-1.688-2.063-2.875c.438.063.813.125 1.125.125.5 0 1-.063 1.5-.25-1.313-.25-2.438-.938-3.313-1.938a5.673 5.673 0 0 1-1.313-3.688v-.063c.813.438 1.688.688 2.625.688a5.228 5.228 0 0 1-1.875-2c-.5-.875-.688-1.813-.688-2.75 0-1.063.25-2.063.75-2.938 1.438 1.75 3.188 3.188 5.25 4.25s4.313 1.688 6.688 1.813a5.579 5.579 0 0 1 1.5-5.438c1.125-1.125 2.5-1.688 4.125-1.688s3.063.625 4.188 1.813a11.48 11.48 0 0 0 3.688-1.375c-.438 1.375-1.313 2.438-2.563 3.188 1.125-.125 2.188-.438 3.313-.875z" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<style lang="scss" is:global>
|
||||||
|
.gh-head {
|
||||||
|
padding: 1vmin 4vmin;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
line-height: 1.3em;
|
||||||
|
color: #fff;
|
||||||
|
background: var(--ghost-accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-head a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-head-inner {
|
||||||
|
display: grid;
|
||||||
|
grid-gap: 2.5vmin;
|
||||||
|
grid-template-columns: auto auto 1fr;
|
||||||
|
grid-auto-flow: row dense;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Brand
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
.gh-head-brand {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 40px;
|
||||||
|
max-width: 200px;
|
||||||
|
text-align: center;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-head-logo {
|
||||||
|
display: block;
|
||||||
|
padding: 10px 0;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 2rem;
|
||||||
|
line-height: 1.2em;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-head-logo img {
|
||||||
|
max-height: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Primary Navigation
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
.gh-head-menu {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-head-menu .nav {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-head-menu .nav li {
|
||||||
|
margin: 0 2.5vmin 0 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-head-menu .nav a {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 5px 0;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-head-menu .nav a:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Secondary Navigation
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
.gh-head-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
list-style: none;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-head-actions-list {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-head-actions-list a:not([class]) {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 0 0 1.5vmin;
|
||||||
|
padding: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-social {
|
||||||
|
margin: 0 1.5vmin 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-social a {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-social a + a {
|
||||||
|
margin-left: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-social a:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-social svg {
|
||||||
|
height: 22px;
|
||||||
|
width: 22px;
|
||||||
|
fill: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-social-facebook svg {
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.gh-head-button {
|
||||||
|
display: block;
|
||||||
|
padding: 8px 15px;
|
||||||
|
color: var(--color-darkgrey);
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: -0.015em;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
line-height: 1em;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile Menu Trigger
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
.gh-burger {
|
||||||
|
position: relative;
|
||||||
|
display: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-burger-box {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 33px;
|
||||||
|
height: 33px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-burger-inner {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-burger-box::before {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto;
|
||||||
|
content: "";
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
background: currentcolor;
|
||||||
|
transition: transform 300ms cubic-bezier(0.2, 0.6, 0.3, 1),
|
||||||
|
width 300ms cubic-bezier(0.2, 0.6, 0.3, 1);
|
||||||
|
will-change: transform, width;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-burger-inner::before,
|
||||||
|
.gh-burger-inner::after {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto;
|
||||||
|
content: "";
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
background: currentcolor;
|
||||||
|
transition: transform 250ms cubic-bezier(0.2, 0.7, 0.3, 1),
|
||||||
|
width 250ms cubic-bezier(0.2, 0.7, 0.3, 1);
|
||||||
|
will-change: transform, width;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-burger-inner::before {
|
||||||
|
transform: translatey(-6px);
|
||||||
|
}
|
||||||
|
.gh-burger-inner::after {
|
||||||
|
transform: translatey(6px);
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.gh-head-open) .gh-burger:hover .gh-burger-inner::before {
|
||||||
|
transform: translatey(-8px);
|
||||||
|
}
|
||||||
|
body:not(.gh-head-open) .gh-burger:hover .gh-burger-inner::after {
|
||||||
|
transform: translatey(8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-head-open .gh-burger-box::before {
|
||||||
|
width: 0;
|
||||||
|
transform: translatex(19px);
|
||||||
|
transition: transform 200ms cubic-bezier(0.2, 0.7, 0.3, 1),
|
||||||
|
width 200ms cubic-bezier(0.2, 0.7, 0.3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-head-open .gh-burger-inner::before {
|
||||||
|
width: 26px;
|
||||||
|
transform: translatex(6px) rotate(135deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-head-open .gh-burger-inner::after {
|
||||||
|
width: 26px;
|
||||||
|
transform: translatex(6px) rotate(-135deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile Menu
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
/* IDs needed to ensure sufficient specificity */
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.gh-burger {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
#gh-head {
|
||||||
|
transition: all 0.4s ease-out;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
#gh-head .gh-head-inner {
|
||||||
|
height: 100%;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
#gh-head .gh-head-brand {
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
grid-column-start: auto;
|
||||||
|
max-width: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.home-template #gh-head .gh-head-brand {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
#gh-head .gh-head-menu {
|
||||||
|
align-self: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0 0 10vh 0;
|
||||||
|
font-weight: 300;
|
||||||
|
font-size: 3.6rem;
|
||||||
|
line-height: 1.1em;
|
||||||
|
}
|
||||||
|
#gh-head .gh-head-menu .nav li {
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
#gh-head .gh-head-menu .nav a {
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
#gh-head .gh-head-menu .nav {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
#gh-head .gh-head-actions {
|
||||||
|
padding: 20px 0;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
#gh-head .gh-head-actions a {
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
/* Hide collapsed content */
|
||||||
|
#gh-head .gh-head-actions,
|
||||||
|
#gh-head .gh-head-menu {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
/* Open the menu */
|
||||||
|
.gh-head-open {
|
||||||
|
overflow: hidden;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
.gh-head-open #gh-head {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 3999999;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
.gh-head-open #gh-head .gh-head-inner {
|
||||||
|
grid-template-rows: auto 1fr auto;
|
||||||
|
}
|
||||||
|
.gh-head-open #gh-head .gh-head-actions,
|
||||||
|
.gh-head-open #gh-head .gh-head-menu {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
#gh-head .gh-head-menu {
|
||||||
|
font-size: 6vmin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-template .gh-head {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 2000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-template .gh-head.has-cover {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-template.gh-head-open .gh-head {
|
||||||
|
background: var(--ghost-accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-template .gh-head-logo {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.home-template .gh-head-menu {
|
||||||
|
margin-left: -2.5vmin;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
if (!window.handleMenu) {
|
||||||
|
window.handleMenu = () => {
|
||||||
|
// menu
|
||||||
|
const burgerButton = document.querySelector(".gh-burger");
|
||||||
|
if (burgerButton) {
|
||||||
|
burgerButton.addEventListener("click", () => {
|
||||||
|
const body = document.querySelector("body");
|
||||||
|
body.classList.toggle("gh-head-open");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const callback = () => {
|
||||||
|
window.handleMenu();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (document.readyState === "complete") {
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
document.addEventListener("DOMContentLoaded", callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
|
@ -0,0 +1,154 @@
|
||||||
|
---
|
||||||
|
import { getGhostImgPath } from "../utils";
|
||||||
|
import type { Settings } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
import cover from "./cover.jpg";
|
||||||
|
|
||||||
|
export type Props = {
|
||||||
|
featureImg: string;
|
||||||
|
mainTitle?: string;
|
||||||
|
settings: Settings;
|
||||||
|
description?: string;
|
||||||
|
addClass?: string;
|
||||||
|
};
|
||||||
|
const {
|
||||||
|
featureImg = cover.src,
|
||||||
|
mainTitle = "",
|
||||||
|
settings,
|
||||||
|
description = "",
|
||||||
|
} = Astro.props as Props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="site-header-content">
|
||||||
|
{featureImg && (
|
||||||
|
<img
|
||||||
|
class="site-header-cover"
|
||||||
|
data-srcset={`
|
||||||
|
${getGhostImgPath(settings.url, featureImg, 300)} 300w,
|
||||||
|
${getGhostImgPath(settings.url, featureImg, 600)} 600w
|
||||||
|
${getGhostImgPath(settings.url, featureImg, 1000)} 1000w
|
||||||
|
${getGhostImgPath(settings.url, featureImg, 2000)} 2000w
|
||||||
|
`}
|
||||||
|
srcset={`
|
||||||
|
${getGhostImgPath(settings.url, featureImg, 300)} 300w,
|
||||||
|
${getGhostImgPath(settings.url, featureImg, 600)} 600w
|
||||||
|
${getGhostImgPath(settings.url, featureImg, 1000)} 1000w
|
||||||
|
${getGhostImgPath(settings.url, featureImg, 2000)} 2000w
|
||||||
|
`}
|
||||||
|
data-sizes="auto"
|
||||||
|
data-src={getGhostImgPath(settings.url, featureImg, 2000)}
|
||||||
|
src={getGhostImgPath(settings.url, featureImg, 2000)}
|
||||||
|
alt={mainTitle}
|
||||||
|
sizes="100vw"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{!featureImg && (
|
||||||
|
<img
|
||||||
|
class="site-header-cover"
|
||||||
|
data-srcset={`
|
||||||
|
${cover.src} 300w,
|
||||||
|
${cover.src} 600w,
|
||||||
|
${cover.src} 1000w,
|
||||||
|
${cover.src} 2000w
|
||||||
|
`}
|
||||||
|
srcset={`
|
||||||
|
${cover.src} 300w,
|
||||||
|
${cover.src} 600w
|
||||||
|
${cover.src} 1000w
|
||||||
|
${cover.src} 2000w
|
||||||
|
`}
|
||||||
|
data-sizes="auto"
|
||||||
|
data-src={cover.src}
|
||||||
|
src={cover.src}
|
||||||
|
alt={mainTitle}
|
||||||
|
sizes="100vw"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<slot name="title">
|
||||||
|
<h1 class="site-title">
|
||||||
|
{settings.logo ? (
|
||||||
|
<img
|
||||||
|
class="site-logo"
|
||||||
|
src={getGhostImgPath(settings.url, settings.logo, 300)}
|
||||||
|
alt={settings.title}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
settings.title
|
||||||
|
)}
|
||||||
|
</h1>
|
||||||
|
</slot>
|
||||||
|
<p>{description}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.site-header {
|
||||||
|
position: relative;
|
||||||
|
color: #fff;
|
||||||
|
background: var(--ghost-accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-header-cover {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-header-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 100;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 6vw 3vw;
|
||||||
|
min-height: 200px;
|
||||||
|
max-height: 340px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-title {
|
||||||
|
z-index: 10;
|
||||||
|
margin: 0 0 0.15em;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-logo {
|
||||||
|
max-height: 55px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-header-content p {
|
||||||
|
z-index: 10;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
line-height: 1.2em;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.site-header-content p {
|
||||||
|
max-width: 80vmin;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4.1 Home header
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
.site-home-header {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1000;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-header-content {
|
||||||
|
padding: 18vmin 4vmin;
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #fff;
|
||||||
|
background: var(--ghost-accent-color);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
import Header from "../components/Header.astro";
|
||||||
|
import Footer from "../components/Footer.astro";
|
||||||
|
import type { Settings } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
export type Props = {
|
||||||
|
settings: Settings;
|
||||||
|
};
|
||||||
|
const { settings } = Astro.props as Props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="viewport">
|
||||||
|
<Header settings={settings} />
|
||||||
|
<div class="site-content">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
<Footer settings={settings} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.content {
|
||||||
|
min-height: 580px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,569 @@
|
||||||
|
---
|
||||||
|
import FeatureImage from "../components/FeatureImage.astro";
|
||||||
|
import type { Settings, Page } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
export type Props = {
|
||||||
|
page: Page;
|
||||||
|
settings: Settings;
|
||||||
|
pageClass?: string;
|
||||||
|
};
|
||||||
|
const { page, settings, pageClass } = Astro.props as Props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<main id="site-main" class="site-main">
|
||||||
|
<article class={`article page ${pageClass}`}>
|
||||||
|
<header class="article-header gh-canvas">
|
||||||
|
{page.feature_image && (
|
||||||
|
<FeatureImage
|
||||||
|
image={page.feature_image}
|
||||||
|
alt={page.feature_image_alt ? page.feature_image_alt : page.title}
|
||||||
|
caption={page.feature_image_caption || ""}
|
||||||
|
settings={settings}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="gh-content gh-canvas">
|
||||||
|
<h1 class="article-title">{page.title}</h1>
|
||||||
|
<Fragment set:html={page.html} />
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<style lang="scss" is:global>
|
||||||
|
.article {
|
||||||
|
padding: 8vmin 0;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-header {
|
||||||
|
padding: 0 0 6vmin 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-tag {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0 0 0.5rem;
|
||||||
|
color: var(--color-midgrey);
|
||||||
|
font-size: 1.3rem;
|
||||||
|
line-height: 1.4em;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-tag a {
|
||||||
|
color: var(--ghost-accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-title {
|
||||||
|
color: color-mod(var(--color-darkgrey) l(-5%));
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-excerpt {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
font-size: 2rem;
|
||||||
|
line-height: 1.4em;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-canvas .article-image {
|
||||||
|
grid-column: wide-start / wide-end;
|
||||||
|
width: 100%;
|
||||||
|
margin: 6vmin 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-canvas .article-image img {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.article-excerpt {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------- */
|
||||||
|
|
||||||
|
/* Content grid
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* Canvas creates a multi-column, centered grid which the post
|
||||||
|
is laid out on top of. Canvas just defines the grid, we don't
|
||||||
|
use it for applying any other styles. */
|
||||||
|
|
||||||
|
.gh-canvas {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns:
|
||||||
|
[full-start]
|
||||||
|
minmax(4vmin, auto)
|
||||||
|
[wide-start]
|
||||||
|
minmax(auto, 240px)
|
||||||
|
[main-start]
|
||||||
|
min(720px, calc(100% - 8vw))
|
||||||
|
[main-end]
|
||||||
|
minmax(auto, 240px)
|
||||||
|
[wide-end]
|
||||||
|
minmax(4vmin, auto)
|
||||||
|
[full-end];
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-canvas > * {
|
||||||
|
grid-column: main-start / main-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-width-wide {
|
||||||
|
grid-column: wide-start / wide-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-width-full {
|
||||||
|
grid-column: full-start / full-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-width-full img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Default vertical spacing */
|
||||||
|
.gh-content > * + * {
|
||||||
|
margin-top: 4vmin;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [id] represents all headings h1-h6, reset all margins */
|
||||||
|
.gh-content > [id] {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--color-darkgrey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add back a top margin to all headings, unless a heading
|
||||||
|
is the very first element in the post content */
|
||||||
|
.gh-content > [id]:not(:first-child) {
|
||||||
|
margin: 2em 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add a small margin between a heading and anything after it */
|
||||||
|
.gh-content > [id] + * {
|
||||||
|
margin-top: 1.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A larger margin before/after HRs and blockquotes */
|
||||||
|
.gh-content > hr,
|
||||||
|
.gh-content > blockquote {
|
||||||
|
position: relative;
|
||||||
|
margin-top: 6vmin;
|
||||||
|
}
|
||||||
|
.gh-content > hr + *,
|
||||||
|
.gh-content > blockquote + * {
|
||||||
|
margin-top: 6vmin !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now the content typography styles */
|
||||||
|
.gh-content a {
|
||||||
|
color: var(--ghost-accent-color);
|
||||||
|
text-decoration: underline;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-content > blockquote,
|
||||||
|
.gh-content > ol,
|
||||||
|
.gh-content > ul,
|
||||||
|
.gh-content > dl,
|
||||||
|
.gh-content > p {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 2.1rem;
|
||||||
|
line-height: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-content > ul,
|
||||||
|
.gh-content > ol,
|
||||||
|
.gh-content > dl {
|
||||||
|
padding-left: 1.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-content > blockquote {
|
||||||
|
position: relative;
|
||||||
|
font-style: italic;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-content > blockquote::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -1.5em;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 0.3rem;
|
||||||
|
background: var(--ghost-accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-content :not(pre) > code {
|
||||||
|
vertical-align: middle;
|
||||||
|
padding: 0.15em 0.4em 0.15em;
|
||||||
|
border: #e1eaef 1px solid;
|
||||||
|
font-weight: 400 !important;
|
||||||
|
font-size: 0.9em;
|
||||||
|
line-height: 1em;
|
||||||
|
color: #15171a;
|
||||||
|
background: #f0f6f9;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-content pre {
|
||||||
|
overflow: auto;
|
||||||
|
padding: 16px 20px;
|
||||||
|
color: var(--color-wash);
|
||||||
|
font-size: 1.4rem;
|
||||||
|
line-height: 1.5em;
|
||||||
|
background: var(--color-darkgrey);
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 2px 6px -2px rgba(0, 0, 0, 0.1), 0 0 1px rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 650px) {
|
||||||
|
.gh-content blockquote,
|
||||||
|
.gh-content ol,
|
||||||
|
.gh-content ul,
|
||||||
|
.gh-content dl,
|
||||||
|
.gh-content p {
|
||||||
|
font-size: 1.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-content blockquote::before {
|
||||||
|
left: -4vmin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cards
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* Cards are dynamic blocks of content which appear within Ghost
|
||||||
|
posts, for example: embedded videos, tweets, galleries, or
|
||||||
|
specially styled bookmark links. We add extra styling here to
|
||||||
|
make sure they look good, and are given a bit of extra spacing. */
|
||||||
|
|
||||||
|
/* Add extra margin before/after any cards,
|
||||||
|
except for when immediately preceeded by a heading */
|
||||||
|
.gh-content :not(.kg-card):not([id]) + .kg-card {
|
||||||
|
margin-top: 6vmin;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.gh-content .kg-card + :not(.kg-card) {
|
||||||
|
margin-top: 6vmin;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This keeps small embeds centered */
|
||||||
|
.kg-embed-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This keeps small iamges centered */
|
||||||
|
.kg-image-card img {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Captions */
|
||||||
|
figcaption {
|
||||||
|
padding: 1.5rem 1.5rem 0;
|
||||||
|
text-align: center;
|
||||||
|
color: rgba(0, 0, 0, 0.5);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
line-height: 1.4em;
|
||||||
|
}
|
||||||
|
figcaption strong {
|
||||||
|
color: rgba(0, 0, 0, 0.8);
|
||||||
|
}
|
||||||
|
figcaption a {
|
||||||
|
color: var(--ghost-accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Highly specific styles for traditional Instagram embeds */
|
||||||
|
iframe.instagram-media {
|
||||||
|
margin-top: 6vmin !important;
|
||||||
|
margin-left: auto !important;
|
||||||
|
margin-right: auto !important;
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe.instagram-media + script + :not([id]) {
|
||||||
|
margin-top: 6vmin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Galleries
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* When there galleries are mixed with images, reduce margin
|
||||||
|
between them, so it looks like 1 big gallery */
|
||||||
|
.kg-image-card + .kg-gallery-card,
|
||||||
|
.kg-gallery-card + .kg-image-card,
|
||||||
|
.kg-gallery-card + .kg-gallery-card {
|
||||||
|
margin-top: 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-image-card.kg-card-hascaption + .kg-gallery-card,
|
||||||
|
.kg-gallery-card.kg-card-hascaption + .kg-image-card,
|
||||||
|
.kg-gallery-card.kg-card-hascaption + .kg-gallery-card {
|
||||||
|
margin-top: 1.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-gallery-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-gallery-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-gallery-image img {
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-gallery-row:not(:first-of-type) {
|
||||||
|
margin: 0.75em 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-gallery-image:not(:first-of-type) {
|
||||||
|
margin: 0 0 0 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bookmark Cards
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* These are styled links with structured data, similar to a
|
||||||
|
Twitter card. These styles roughly match what you see in the
|
||||||
|
Ghost editor. */
|
||||||
|
|
||||||
|
.kg-bookmark-card,
|
||||||
|
.kg-bookmark-publisher {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-container,
|
||||||
|
.kg-bookmark-container:hover {
|
||||||
|
display: flex;
|
||||||
|
color: currentColor;
|
||||||
|
font-family: var(--font-sans-serif);
|
||||||
|
text-decoration: none !important;
|
||||||
|
background: rgba(255, 255, 255, 0.6);
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 2px 6px -2px rgba(0, 0, 0, 0.1), 0 0 1px rgba(0, 0, 0, 0.4);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-basis: 100%;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
line-height: 1.4em;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #15171a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-description {
|
||||||
|
display: -webkit-box;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
line-height: 1.5em;
|
||||||
|
margin-top: 3px;
|
||||||
|
color: #626d79;
|
||||||
|
font-weight: 400;
|
||||||
|
max-height: 44px;
|
||||||
|
overflow-y: hidden;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-metadata {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 22px;
|
||||||
|
width: 100%;
|
||||||
|
color: #394047;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-author,
|
||||||
|
.kg-bookmark-publisher {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-publisher {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 240px;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: block;
|
||||||
|
line-height: 1.65em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-metadata > span:nth-of-type(2) {
|
||||||
|
color: #626d79;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-metadata > span:nth-of-type(2):before {
|
||||||
|
content: "•";
|
||||||
|
color: #394047;
|
||||||
|
margin: 0 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-thumbnail {
|
||||||
|
position: relative;
|
||||||
|
flex-grow: 1;
|
||||||
|
min-width: 33%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-thumbnail img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
border-radius: 0 4px 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card captions
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
.kg-width-full.kg-card-hascaption {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-width-wide.kg-card-hascaption img {
|
||||||
|
grid-column: wide-start / wide-end;
|
||||||
|
}
|
||||||
|
.kg-width-full.kg-card-hascaption img {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-width-full.kg-card-hascaption figcaption {
|
||||||
|
grid-column: main-start / main-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-comments {
|
||||||
|
margin: 6vmin 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -----old------ */
|
||||||
|
|
||||||
|
.footnotes-sep {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footnotes {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footnotes p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footnote-backref {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tables */
|
||||||
|
.post-full-content table {
|
||||||
|
display: inline-block;
|
||||||
|
overflow-x: auto;
|
||||||
|
margin: 0.5em 0 2.5em;
|
||||||
|
max-width: 100%;
|
||||||
|
width: auto;
|
||||||
|
border-spacing: 0;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-family: var(--font-sans-serif);
|
||||||
|
font-size: 1.6rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-full-content table {
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
background: radial-gradient(
|
||||||
|
ellipse at left,
|
||||||
|
rgba(0, 0, 0, 0.2) 0%,
|
||||||
|
rgba(0, 0, 0, 0) 75%
|
||||||
|
)
|
||||||
|
0 center,
|
||||||
|
radial-gradient(
|
||||||
|
ellipse at right,
|
||||||
|
rgba(0, 0, 0, 0.2) 0%,
|
||||||
|
rgba(0, 0, 0, 0) 75%
|
||||||
|
)
|
||||||
|
100% center;
|
||||||
|
background-attachment: scroll, scroll;
|
||||||
|
background-size: 10px 100%, 10px 100%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-full-content table td:first-child {
|
||||||
|
background-image: linear-gradient(
|
||||||
|
to right,
|
||||||
|
rgba(255, 255, 255, 1) 50%,
|
||||||
|
rgba(255, 255, 255, 0) 100%
|
||||||
|
);
|
||||||
|
background-size: 20px 100%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-full-content table td:last-child {
|
||||||
|
background-image: linear-gradient(
|
||||||
|
to left,
|
||||||
|
rgba(255, 255, 255, 1) 50%,
|
||||||
|
rgba(255, 255, 255, 0) 100%
|
||||||
|
);
|
||||||
|
background-position: 100% 0;
|
||||||
|
background-size: 20px 100%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-full-content table th {
|
||||||
|
color: var(--color-darkgrey);
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.2px;
|
||||||
|
text-align: left;
|
||||||
|
text-transform: uppercase;
|
||||||
|
background-color: color-mod(var(--color-wash) l(+4%));
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-full-content table th,
|
||||||
|
.post-full-content table td {
|
||||||
|
padding: 6px 12px;
|
||||||
|
border: color-mod(var(--color-wash) l(-1%) s(-5%)) 1px solid;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
import type { Page } from 'astro';
|
||||||
|
const { page } = Astro.props as {page: Page};
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="page__actions">
|
||||||
|
{page.url.prev && (
|
||||||
|
<a class="action__go-to-x" href={page.url.prev} title="Go to Previous">
|
||||||
|
← Prev
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{page.url.next && (
|
||||||
|
<a class="action__go-to-x" href={page.url.next} title="Go to Next">
|
||||||
|
Next →
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* .page__actions {
|
||||||
|
@apply flex justify-center md:justify-end py-6 gap-2;
|
||||||
|
}
|
||||||
|
.action__go-to-x {
|
||||||
|
@apply text-base uppercase text-gray-500 dark:text-gray-400 hover:underline;
|
||||||
|
} */
|
||||||
|
</style>
|
|
@ -0,0 +1,569 @@
|
||||||
|
---
|
||||||
|
import PostHero from "../components/PostHero.astro";
|
||||||
|
import PostFooter from "../components/PostFooter.astro";
|
||||||
|
import {invariant, type Post, type Settings } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
export type Props = {
|
||||||
|
post: Post;
|
||||||
|
settings: Settings;
|
||||||
|
postClass?: string;
|
||||||
|
posts: Post[];
|
||||||
|
};
|
||||||
|
const { post, settings, postClass, posts } = Astro.props as Props;
|
||||||
|
invariant(settings, "Settings not found");
|
||||||
|
---
|
||||||
|
|
||||||
|
<main id="site-main" class="site-main">
|
||||||
|
<article class={`article post ${postClass}`}>
|
||||||
|
<PostHero post={post} settings={settings} />
|
||||||
|
<section class="gh-content gh-canvas">
|
||||||
|
<Fragment set:html={post.html} />
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
<PostFooter post={post} settings={settings} posts={posts} />
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<style lang="scss" is:global>
|
||||||
|
.article {
|
||||||
|
padding: 8vmin 0;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-header {
|
||||||
|
padding: 0 0 6vmin 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-tag {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0 0 0.5rem;
|
||||||
|
color: var(--color-midgrey);
|
||||||
|
font-size: 1.3rem;
|
||||||
|
line-height: 1.4em;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-tag a {
|
||||||
|
color: var(--ghost-accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-title {
|
||||||
|
color: color-mod(var(--color-darkgrey) l(-5%));
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-excerpt {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
font-size: 2rem;
|
||||||
|
line-height: 1.4em;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-canvas .article-image {
|
||||||
|
grid-column: wide-start / wide-end;
|
||||||
|
width: 100%;
|
||||||
|
margin: 6vmin 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-canvas .article-image img {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.article-excerpt {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------- */
|
||||||
|
|
||||||
|
/* Content grid
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* Canvas creates a multi-column, centered grid which the post
|
||||||
|
is laid out on top of. Canvas just defines the grid, we don't
|
||||||
|
use it for applying any other styles. */
|
||||||
|
|
||||||
|
.gh-canvas {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns:
|
||||||
|
[full-start]
|
||||||
|
minmax(4vmin, auto)
|
||||||
|
[wide-start]
|
||||||
|
minmax(auto, 240px)
|
||||||
|
[main-start]
|
||||||
|
min(720px, calc(100% - 8vw))
|
||||||
|
[main-end]
|
||||||
|
minmax(auto, 240px)
|
||||||
|
[wide-end]
|
||||||
|
minmax(4vmin, auto)
|
||||||
|
[full-end];
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-canvas > * {
|
||||||
|
grid-column: main-start / main-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-width-wide {
|
||||||
|
grid-column: wide-start / wide-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-width-full {
|
||||||
|
grid-column: full-start / full-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-width-full img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Content
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* Content refers to styling all page and post content that is
|
||||||
|
created within the Ghost editor. The main content handles
|
||||||
|
headings, text, images and lists. We deal with cards lower down. */
|
||||||
|
|
||||||
|
/* Default vertical spacing */
|
||||||
|
.gh-content > * + * {
|
||||||
|
margin-top: 4vmin;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [id] represents all headings h1-h6, reset all margins */
|
||||||
|
.gh-content > [id] {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--color-darkgrey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add back a top margin to all headings, unless a heading
|
||||||
|
is the very first element in the post content */
|
||||||
|
.gh-content > [id]:not(:first-child) {
|
||||||
|
margin: 2em 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add a small margin between a heading and anything after it */
|
||||||
|
.gh-content > [id] + * {
|
||||||
|
margin-top: 1.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A larger margin before/after HRs and blockquotes */
|
||||||
|
.gh-content > hr,
|
||||||
|
.gh-content > blockquote {
|
||||||
|
position: relative;
|
||||||
|
margin-top: 6vmin;
|
||||||
|
}
|
||||||
|
.gh-content > hr + *,
|
||||||
|
.gh-content > blockquote + * {
|
||||||
|
margin-top: 6vmin !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now the content typography styles */
|
||||||
|
.gh-content a {
|
||||||
|
color: var(--ghost-accent-color);
|
||||||
|
text-decoration: underline;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-content > blockquote,
|
||||||
|
.gh-content > ol,
|
||||||
|
.gh-content > ul,
|
||||||
|
.gh-content > dl,
|
||||||
|
.gh-content > p {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 2.1rem;
|
||||||
|
line-height: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-content > ul,
|
||||||
|
.gh-content > ol,
|
||||||
|
.gh-content > dl {
|
||||||
|
padding-left: 1.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-content > blockquote {
|
||||||
|
position: relative;
|
||||||
|
font-style: italic;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-content > blockquote::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -1.5em;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 0.3rem;
|
||||||
|
background: var(--ghost-accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-content :not(pre) > code {
|
||||||
|
vertical-align: middle;
|
||||||
|
padding: 0.15em 0.4em 0.15em;
|
||||||
|
border: #e1eaef 1px solid;
|
||||||
|
font-weight: 400 !important;
|
||||||
|
font-size: 0.9em;
|
||||||
|
line-height: 1em;
|
||||||
|
color: #15171a;
|
||||||
|
background: #f0f6f9;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-content pre {
|
||||||
|
overflow: auto;
|
||||||
|
padding: 16px 20px;
|
||||||
|
color: var(--color-wash);
|
||||||
|
font-size: 1.4rem;
|
||||||
|
line-height: 1.5em;
|
||||||
|
background: var(--color-darkgrey);
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 2px 6px -2px rgba(0, 0, 0, 0.1), 0 0 1px rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 650px) {
|
||||||
|
.gh-content blockquote,
|
||||||
|
.gh-content ol,
|
||||||
|
.gh-content ul,
|
||||||
|
.gh-content dl,
|
||||||
|
.gh-content p {
|
||||||
|
font-size: 1.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-content blockquote::before {
|
||||||
|
left: -4vmin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cards
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* Cards are dynamic blocks of content which appear within Ghost
|
||||||
|
posts, for example: embedded videos, tweets, galleries, or
|
||||||
|
specially styled bookmark links. We add extra styling here to
|
||||||
|
make sure they look good, and are given a bit of extra spacing. */
|
||||||
|
|
||||||
|
/* Add extra margin before/after any cards,
|
||||||
|
except for when immediately preceeded by a heading */
|
||||||
|
.gh-content :not(.kg-card):not([id]) + .kg-card {
|
||||||
|
margin-top: 6vmin;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.gh-content .kg-card + :not(.kg-card) {
|
||||||
|
margin-top: 6vmin;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This keeps small embeds centered */
|
||||||
|
.kg-embed-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This keeps small iamges centered */
|
||||||
|
.kg-image-card img {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Captions */
|
||||||
|
figcaption {
|
||||||
|
padding: 1.5rem 1.5rem 0;
|
||||||
|
text-align: center;
|
||||||
|
color: rgba(0, 0, 0, 0.5);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
line-height: 1.4em;
|
||||||
|
}
|
||||||
|
figcaption strong {
|
||||||
|
color: rgba(0, 0, 0, 0.8);
|
||||||
|
}
|
||||||
|
figcaption a {
|
||||||
|
color: var(--ghost-accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Highly specific styles for traditional Instagram embeds */
|
||||||
|
iframe.instagram-media {
|
||||||
|
margin-top: 6vmin !important;
|
||||||
|
margin-left: auto !important;
|
||||||
|
margin-right: auto !important;
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe.instagram-media + script + :not([id]) {
|
||||||
|
margin-top: 6vmin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Galleries
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* When there galleries are mixed with images, reduce margin
|
||||||
|
between them, so it looks like 1 big gallery */
|
||||||
|
.kg-image-card + .kg-gallery-card,
|
||||||
|
.kg-gallery-card + .kg-image-card,
|
||||||
|
.kg-gallery-card + .kg-gallery-card {
|
||||||
|
margin-top: 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-image-card.kg-card-hascaption + .kg-gallery-card,
|
||||||
|
.kg-gallery-card.kg-card-hascaption + .kg-image-card,
|
||||||
|
.kg-gallery-card.kg-card-hascaption + .kg-gallery-card {
|
||||||
|
margin-top: 1.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-gallery-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-gallery-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-gallery-image img {
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-gallery-row:not(:first-of-type) {
|
||||||
|
margin: 0.75em 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-gallery-image:not(:first-of-type) {
|
||||||
|
margin: 0 0 0 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bookmark Cards
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* These are styled links with structured data, similar to a
|
||||||
|
Twitter card. These styles roughly match what you see in the
|
||||||
|
Ghost editor. */
|
||||||
|
|
||||||
|
.kg-bookmark-card,
|
||||||
|
.kg-bookmark-publisher {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-container,
|
||||||
|
.kg-bookmark-container:hover {
|
||||||
|
display: flex;
|
||||||
|
color: currentColor;
|
||||||
|
font-family: var(--font-sans-serif);
|
||||||
|
text-decoration: none !important;
|
||||||
|
background: rgba(255, 255, 255, 0.6);
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 2px 6px -2px rgba(0, 0, 0, 0.1), 0 0 1px rgba(0, 0, 0, 0.4);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-basis: 100%;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
line-height: 1.4em;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #15171a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-description {
|
||||||
|
display: -webkit-box;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
line-height: 1.5em;
|
||||||
|
margin-top: 3px;
|
||||||
|
color: #626d79;
|
||||||
|
font-weight: 400;
|
||||||
|
max-height: 44px;
|
||||||
|
overflow-y: hidden;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-metadata {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 22px;
|
||||||
|
width: 100%;
|
||||||
|
color: #394047;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-author,
|
||||||
|
.kg-bookmark-publisher {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-publisher {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 240px;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: block;
|
||||||
|
line-height: 1.65em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-metadata > span:nth-of-type(2) {
|
||||||
|
color: #626d79;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-metadata > span:nth-of-type(2):before {
|
||||||
|
content: "•";
|
||||||
|
color: #394047;
|
||||||
|
margin: 0 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-thumbnail {
|
||||||
|
position: relative;
|
||||||
|
flex-grow: 1;
|
||||||
|
min-width: 33%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-bookmark-thumbnail img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
border-radius: 0 4px 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card captions
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
.kg-width-full.kg-card-hascaption {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-width-wide.kg-card-hascaption img {
|
||||||
|
grid-column: wide-start / wide-end;
|
||||||
|
}
|
||||||
|
.kg-width-full.kg-card-hascaption img {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-width-full.kg-card-hascaption figcaption {
|
||||||
|
grid-column: main-start / main-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-comments {
|
||||||
|
margin: 6vmin 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -----old------ */
|
||||||
|
|
||||||
|
.footnotes-sep {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footnotes {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footnotes p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footnote-backref {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tables */
|
||||||
|
.post-full-content table {
|
||||||
|
display: inline-block;
|
||||||
|
overflow-x: auto;
|
||||||
|
margin: 0.5em 0 2.5em;
|
||||||
|
max-width: 100%;
|
||||||
|
width: auto;
|
||||||
|
border-spacing: 0;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-family: var(--font-sans-serif);
|
||||||
|
font-size: 1.6rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-full-content table {
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
background: radial-gradient(
|
||||||
|
ellipse at left,
|
||||||
|
rgba(0, 0, 0, 0.2) 0%,
|
||||||
|
rgba(0, 0, 0, 0) 75%
|
||||||
|
)
|
||||||
|
0 center,
|
||||||
|
radial-gradient(
|
||||||
|
ellipse at right,
|
||||||
|
rgba(0, 0, 0, 0.2) 0%,
|
||||||
|
rgba(0, 0, 0, 0) 75%
|
||||||
|
)
|
||||||
|
100% center;
|
||||||
|
background-attachment: scroll, scroll;
|
||||||
|
background-size: 10px 100%, 10px 100%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-full-content table td:first-child {
|
||||||
|
background-image: linear-gradient(
|
||||||
|
to right,
|
||||||
|
rgba(255, 255, 255, 1) 50%,
|
||||||
|
rgba(255, 255, 255, 0) 100%
|
||||||
|
);
|
||||||
|
background-size: 20px 100%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-full-content table td:last-child {
|
||||||
|
background-image: linear-gradient(
|
||||||
|
to left,
|
||||||
|
rgba(255, 255, 255, 1) 50%,
|
||||||
|
rgba(255, 255, 255, 0) 100%
|
||||||
|
);
|
||||||
|
background-position: 100% 0;
|
||||||
|
background-size: 20px 100%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-full-content table th {
|
||||||
|
color: var(--color-darkgrey);
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.2px;
|
||||||
|
text-align: left;
|
||||||
|
text-transform: uppercase;
|
||||||
|
background-color: color-mod(var(--color-wash) l(+4%));
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-full-content table th,
|
||||||
|
.post-full-content table td {
|
||||||
|
padding: 6px 12px;
|
||||||
|
border: color-mod(var(--color-wash) l(-1%) s(-5%)) 1px solid;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,125 @@
|
||||||
|
---
|
||||||
|
import PostPreview from "../components/PostPreview.astro";
|
||||||
|
import type { Settings, Post } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
export type Props = {
|
||||||
|
post: Post;
|
||||||
|
settings: Settings;
|
||||||
|
posts: Post[];
|
||||||
|
};
|
||||||
|
const { post, settings, posts } = Astro.props as Props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--section class="footer-cta">
|
||||||
|
<div class="inner">
|
||||||
|
<h2>Sign up for more like this.</h2>
|
||||||
|
<a class="footer-cta-button" href="#/portal">
|
||||||
|
<div>Enter your email</div>
|
||||||
|
<span>Subscribe</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section-->
|
||||||
|
|
||||||
|
<aside class="read-more-wrap">
|
||||||
|
<div class="read-more inner">
|
||||||
|
{posts
|
||||||
|
.filter((p: Post) => p.id !== post.id)
|
||||||
|
.slice(0, 3)
|
||||||
|
.map((post: Post) => <PostPreview post={post} settings={settings} />)}
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
/* 7.3. Subscribe
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
.footer-cta {
|
||||||
|
position: relative;
|
||||||
|
padding: 9vmin 4vmin 10vmin;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
background: var(--color-darkgrey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Increases the default h2 size by 15%, for small and large screens */
|
||||||
|
.footer-cta h2 {
|
||||||
|
margin: 0 0 30px;
|
||||||
|
font-size: 3.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.footer-cta h2 {
|
||||||
|
font-size: 2.65rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-cta-button {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 500px;
|
||||||
|
padding: 5px 5px 5px 15px;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
color: var(--color-midgrey);
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-cta-button span {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 10px 20px;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 500;
|
||||||
|
background: var(--ghost-accent-color);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 7.4. Read more
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
.read-more-wrap {
|
||||||
|
width: 100%;
|
||||||
|
padding: 4vmin;
|
||||||
|
margin: 0 auto -40px;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
background: color-mod(var(--color-darkgrey) l(-5%));
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
grid-gap: 4vmin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more .post-card-title {
|
||||||
|
color: #fff;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more .post-card-excerpt {
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more .post-card-byline-content a {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1000px) {
|
||||||
|
.read-more {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
.read-more article:nth-child(3) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
.read-more {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
.read-more article:nth-child(2) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,108 @@
|
||||||
|
---
|
||||||
|
import FeatureImage from "../components/FeatureImage.astro";
|
||||||
|
import AuthorList from "../components/AuthorList.astro";
|
||||||
|
import { formatDate } from "../utils";
|
||||||
|
import type { Settings, Post } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
export type Props = {
|
||||||
|
post: Post;
|
||||||
|
settings: Settings;
|
||||||
|
};
|
||||||
|
const { post, settings } = Astro.props as Props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<header class="article-header gh-canvas">
|
||||||
|
{post.primary_tag && (
|
||||||
|
<section class="article-tag">
|
||||||
|
<a href={`/tag/${post.primary_tag.slug}`}>{post.primary_tag.name}</a>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
<h1 class="article-title" transition:name={post.title}>{post.title}</h1>
|
||||||
|
|
||||||
|
{post.custom_excerpt && <p class="article-excerpt">{post.custom_excerpt}</p>}
|
||||||
|
<div class="article-byline">
|
||||||
|
<section class="article-byline-content">
|
||||||
|
<AuthorList post={post} settings={settings} />
|
||||||
|
<div class="article-byline-meta">
|
||||||
|
{ post.authors && post.authors.length > 1 && (
|
||||||
|
<h4 class="author-name">
|
||||||
|
{post.authors.map((author) => author.name).join(", ")}
|
||||||
|
</h4>
|
||||||
|
)}
|
||||||
|
<div class="byline-meta-content">
|
||||||
|
<time class="byline-meta-date" datetime={formatDate(post.created_at)}
|
||||||
|
>{formatDate(post.created_at)}
|
||||||
|
</time>
|
||||||
|
<span class="byline-reading-time"
|
||||||
|
><span class="bull">•</span>
|
||||||
|
{post.reading_time} min read
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
{post.feature_image && (
|
||||||
|
<FeatureImage
|
||||||
|
image={post.feature_image}
|
||||||
|
alt={post.feature_image_alt ? post.feature_image_alt : post.title}
|
||||||
|
caption={post.feature_image_caption || "" }
|
||||||
|
settings={settings}
|
||||||
|
transitionName={`img-${post.title}`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.article-byline {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 20px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-byline-content {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-byline-content .author-list {
|
||||||
|
justify-content: flex-start;
|
||||||
|
padding: 0 12px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-byline-meta {
|
||||||
|
color: color-mod(var(--color-midgrey));
|
||||||
|
font-size: 1.4rem;
|
||||||
|
line-height: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-byline-meta h4 {
|
||||||
|
margin: 0 0 3px;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-byline-meta .bull {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 2px;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.basic-info .avatar-wrapper {
|
||||||
|
position: relative;
|
||||||
|
margin: 0;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border: none;
|
||||||
|
background: rgba(229, 239, 245, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.basic-info .avatar-wrapper svg {
|
||||||
|
margin: 0;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
opacity: 0.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-template .article-title {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,301 @@
|
||||||
|
---
|
||||||
|
import { getGhostImgPath, formatDate } from "../utils";
|
||||||
|
import AuthorList from "../components/AuthorList.astro";
|
||||||
|
import type { Settings, Post, Tag } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
export type Props = {
|
||||||
|
post: Post;
|
||||||
|
settings: Settings;
|
||||||
|
index?: number;
|
||||||
|
isHome?: boolean;
|
||||||
|
};
|
||||||
|
const { post, settings, index, isHome = false } = Astro.props as Props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<article
|
||||||
|
class={`post-card ${post.tags && post.tags
|
||||||
|
.map((tag: Tag) => `tag-${tag.slug}`)
|
||||||
|
.join(" ")} ${
|
||||||
|
isHome && post.feature_image && index == 0 ? "post-card-large" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<a class="post-card-image-link" href={`/${post.slug}`} data-astro-reload>
|
||||||
|
<img
|
||||||
|
class="post-card-image"
|
||||||
|
srcset={`
|
||||||
|
${getGhostImgPath(settings.url, post.feature_image || "", 300)} 300w,
|
||||||
|
${getGhostImgPath(settings.url, post.feature_image || "", 600)} 600w
|
||||||
|
${getGhostImgPath(settings.url, post.feature_image || "", 1000)} 1000w
|
||||||
|
${getGhostImgPath(settings.url, post.feature_image || "", 2000)} 2000w
|
||||||
|
`}
|
||||||
|
src={`${getGhostImgPath(settings.url, post.feature_image || "", 600)}`}
|
||||||
|
alt={post.title}
|
||||||
|
sizes="(max-width: 1000px) 400px, 800px"
|
||||||
|
loading="lazy"
|
||||||
|
transition:name={`img-${post.title}`}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<div class="post-card-content">
|
||||||
|
<a class="post-card-content-link" href={`/${post.slug}`} data-astro-reload>
|
||||||
|
<header class="post-card-header">
|
||||||
|
{post.primary_tag && (
|
||||||
|
<div class="post-card-primary-tag">{post.primary_tag.name}</div>
|
||||||
|
)}
|
||||||
|
<h2 class="post-card-title" transition:name={post.title}>{post.title}</h2>
|
||||||
|
</header>
|
||||||
|
<div class="post-card-excerpt">
|
||||||
|
<p>{post.excerpt}</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<footer class="post-card-meta">
|
||||||
|
<AuthorList post={post} settings={settings} />
|
||||||
|
<div class="post-card-byline-content">
|
||||||
|
<span>{post.primary_author?.name ?? ""}</span>
|
||||||
|
<span class="post-card-byline-date"
|
||||||
|
><time datetime={formatDate(post.created_at)}
|
||||||
|
>{formatDate(post.created_at)}
|
||||||
|
</time>
|
||||||
|
<span class="bull">•</span>
|
||||||
|
{post.reading_time} min read
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<style lang="scss" is:global>
|
||||||
|
@use "sass:color";
|
||||||
|
@import "../styles/variables";
|
||||||
|
.post-card {
|
||||||
|
position: relative;
|
||||||
|
flex: 1 1 301px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 220px;
|
||||||
|
background-size: cover;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-excerpt {
|
||||||
|
max-width: 56em;
|
||||||
|
color: color.scale($color-midgrey, $lightness: -8%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-image-link {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
background: $color-lightgrey no-repeat center center;
|
||||||
|
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-content-link {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
color: $color-darkgrey;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-content-link:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-header {
|
||||||
|
margin: 20px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-feed .no-image .post-card-content-link {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-image .post-card-header {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-primary-tag {
|
||||||
|
margin: 0 0 0.2em;
|
||||||
|
color: var(--ghost-accent-color);
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.2px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-title {
|
||||||
|
margin: 0 0 0.4em;
|
||||||
|
font-size: 2.4rem;
|
||||||
|
transition: color 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-image .post-card-title {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-content {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-excerpt p {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
display: -webkit-box;
|
||||||
|
overflow-y: hidden;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-meta {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-wrapper {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: color.scale($color-lightgrey, $lightness: +8%);
|
||||||
|
border-radius: 100%;
|
||||||
|
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-meta .profile-image-wrapper,
|
||||||
|
.post-card-meta .avatar-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.static-avatar {
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 0 0 0 -6px;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 100%;
|
||||||
|
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-byline-content {
|
||||||
|
flex: 1 1 50%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 0 0 0 8px;
|
||||||
|
color: color.scale($color-midgrey, $lightness: +10%);
|
||||||
|
font-size: 1.4rem;
|
||||||
|
line-height: 1.2em;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-byline-content span {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-byline-content a {
|
||||||
|
color: color.scale($color-darkgrey, $lightness: +15%);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-byline-date {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-byline-date .bull {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 2px;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-author-byline {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-left: 5px;
|
||||||
|
color: color.scale($color-midgrey, $lightness: -10%);
|
||||||
|
font-size: 1.3rem;
|
||||||
|
line-height: 1.4em;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-author {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-author .static-avatar {
|
||||||
|
margin-left: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-author-name {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Special Styling for home page grid (below):
|
||||||
|
|
||||||
|
The first post in the list is styled to be bigger than the others and take over
|
||||||
|
the full width of the grid to give it more emphasis. Wrapped in a media query to
|
||||||
|
make sure this only happens on large viewports / desktop-ish devices.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
@media (min-width: 1001px) {
|
||||||
|
.post-card-large {
|
||||||
|
grid-column: 1 / span 3;
|
||||||
|
display: grid;
|
||||||
|
grid-gap: 4vmin;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
min-height: 280px;
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-large:not(.no-image) .post-card-header {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-large .post-card-image-link {
|
||||||
|
position: relative;
|
||||||
|
grid-column: 1 / span 2;
|
||||||
|
margin-bottom: 0;
|
||||||
|
min-height: 380px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-large .post-card-image {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-large .post-card-content {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-large .post-card-title {
|
||||||
|
margin-top: 0;
|
||||||
|
font-size: 3.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-large .post-card-excerpt p {
|
||||||
|
margin-bottom: 1.5em;
|
||||||
|
font-size: 1.7rem;
|
||||||
|
line-height: 1.55em;
|
||||||
|
-webkit-line-clamp: 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
.post-card-title {
|
||||||
|
font-size: 1.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card-excerpt {
|
||||||
|
font-size: 1.6rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,43 @@
|
||||||
|
---
|
||||||
|
import PostPreview from "../components/PostPreview.astro";
|
||||||
|
import type { Settings, Post } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
export type Props = {
|
||||||
|
posts: Post[];
|
||||||
|
settings: Settings;
|
||||||
|
isHome?: boolean;
|
||||||
|
};
|
||||||
|
const { posts, settings, isHome = false } = Astro.props as Props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="post-feed">
|
||||||
|
{posts.map((post: Post, index: number) => (
|
||||||
|
<PostPreview
|
||||||
|
post={post}
|
||||||
|
index={index}
|
||||||
|
settings={settings}
|
||||||
|
isHome={isHome}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.post-feed {
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
grid-gap: 4vmin;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
padding: 4vmin 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1000px) {
|
||||||
|
.post-feed {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
.post-feed {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
grid-gap: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,51 @@
|
||||||
|
---
|
||||||
|
import { getGhostImgPath } from "../utils";
|
||||||
|
import type { Settings, Tag } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
export type Props = {
|
||||||
|
tag: Tag;
|
||||||
|
addClass?: string;
|
||||||
|
settings: Settings;
|
||||||
|
};
|
||||||
|
const { tag, addClass = "", settings } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<a
|
||||||
|
href={`/tag/${tag.slug}`}
|
||||||
|
title={tag.name}
|
||||||
|
aria-label={tag.name}
|
||||||
|
class={`tag-card ${addClass ? addClass : ""}`}
|
||||||
|
style={tag.accent_color ? `--color-accent:${tag.accent_color}` : ""}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
tag.feature_image && (
|
||||||
|
<div class="tag-card-media">
|
||||||
|
<img
|
||||||
|
class="tag-card-img"
|
||||||
|
data-srcset={`
|
||||||
|
${getGhostImgPath(settings.url, tag.feature_image, 100)} 100w,
|
||||||
|
${getGhostImgPath(settings.url, tag.feature_image, 300)} 300w
|
||||||
|
`}
|
||||||
|
srcset={`
|
||||||
|
${getGhostImgPath(settings.url, tag.feature_image, 100)} 100w,
|
||||||
|
${getGhostImgPath(settings.url, tag.feature_image, 300)} 300w
|
||||||
|
`}
|
||||||
|
data-sizes="auto"
|
||||||
|
data-src={getGhostImgPath(settings.url, tag.feature_image, 300)}
|
||||||
|
src={getGhostImgPath(settings.url, tag.feature_image, 300)}
|
||||||
|
alt={tag.name}
|
||||||
|
sizes="200px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<div class="tag-card-content">
|
||||||
|
<h4 class="tag-card-name">{tag.name}</h4>
|
||||||
|
{tag.count && <span class="tag-card-count">{tag.count.posts}+ Posts</span>}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.tag-card {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
Binary file not shown.
After Width: | Height: | Size: 571 KiB |
|
@ -0,0 +1 @@
|
||||||
|
/// <reference types="astro/client" />
|
|
@ -0,0 +1,128 @@
|
||||||
|
---
|
||||||
|
import type { Settings } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
import { AstroFont } from "astro-font";
|
||||||
|
import MainLayout from "../components/MainLayout.astro";
|
||||||
|
import { ViewTransitions } from "astro:transitions";
|
||||||
|
|
||||||
|
export type Props = {
|
||||||
|
content?: {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
bodyClass?: string;
|
||||||
|
settings: Settings;
|
||||||
|
image?: string;
|
||||||
|
permalink?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const { content, permalink, image, settings, bodyClass = "" } = Astro.props as Props;
|
||||||
|
const ghostAccentColor = settings.accent_color;
|
||||||
|
---
|
||||||
|
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<Fragment set:html={settings.codeinjection_head}>
|
||||||
|
|
||||||
|
<AstroFont
|
||||||
|
config={[
|
||||||
|
{
|
||||||
|
src: [],
|
||||||
|
name: "Inter",
|
||||||
|
preload: false,
|
||||||
|
display: "swap",
|
||||||
|
selector: "html",
|
||||||
|
fallback: "sans-serif",
|
||||||
|
googleFontsURL: 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<title>{content ? content.title + "|" + settings.title : settings.title}</title>
|
||||||
|
<ViewTransitions />
|
||||||
|
<meta name="title" content={content ? content.title : settings.title} />
|
||||||
|
{content?.description && <meta name="description" content={content.description} />}
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href={settings.icon} />
|
||||||
|
<link rel="shortcut icon" type="image/png" sizes="16x16" href={settings.icon} />
|
||||||
|
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
||||||
|
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="msapplication-TileColor" content="#da532c" />
|
||||||
|
<meta name="msapplication-config" content="/browserconfig.xml" />
|
||||||
|
<meta name="theme-color" content="#ffffff" />
|
||||||
|
|
||||||
|
<!-- Open Graph Tags (Facebook) -->
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:title" content={content?.title} />
|
||||||
|
{permalink && <meta property="og:url" content={permalink} />}
|
||||||
|
{content?.description && <meta property="og:description" content={content.description} />}
|
||||||
|
{image && <meta property="og:image" content={image} />}
|
||||||
|
|
||||||
|
<!-- Twitter -->
|
||||||
|
<meta property="twitter:card" content="summary_large_image" />
|
||||||
|
<meta property="twitter:title" content={content?.title} />
|
||||||
|
{permalink && <meta property="twitter:url" content={permalink} />}
|
||||||
|
{content?.description && <meta property="twitter:description" content={content.description} />}
|
||||||
|
{image && <meta property="twitter:image" content={image} />}
|
||||||
|
|
||||||
|
<!-- Link to the global style, or the file that imports constructs -->
|
||||||
|
<link
|
||||||
|
rel="preload"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/themes/prism.min.css"
|
||||||
|
as="style"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="preload"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/prism.min.js"
|
||||||
|
as="script"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="preload stylesheet"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/themes/prism.min.css"
|
||||||
|
as="style"
|
||||||
|
onload="this.onload=null;this.rel='stylesheet'"
|
||||||
|
crossorigin
|
||||||
|
/>
|
||||||
|
<script
|
||||||
|
async
|
||||||
|
defer
|
||||||
|
src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/prism.min.js"
|
||||||
|
>
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body class={bodyClass}>
|
||||||
|
<MainLayout settings={settings}>
|
||||||
|
<slot />
|
||||||
|
</MainLayout>
|
||||||
|
<style
|
||||||
|
lang="scss"
|
||||||
|
is:global
|
||||||
|
define:vars={{ "ghost-accent-color": ghostAccentColor }}
|
||||||
|
>
|
||||||
|
@import "../styles/reset.scss";
|
||||||
|
@import "../styles/app.scss";
|
||||||
|
@mixin mq-sm {
|
||||||
|
@media only screen and (min-width: 36em) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@mixin mq-md {
|
||||||
|
@media only screen and (min-width: 48em) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@mixin mq-lg {
|
||||||
|
@media only screen and (min-width: 62em) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@mixin mq-xl {
|
||||||
|
@media only screen and (min-width: 75em) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,45 @@
|
||||||
|
---
|
||||||
|
import type { InferGetStaticPropsType } from 'astro';
|
||||||
|
import DefaultPageLayout from "../layouts/default.astro";
|
||||||
|
import Page from "../components/Page.astro";
|
||||||
|
import Post from "../components/Post.astro";
|
||||||
|
import { getSettings, getAllPages, getAllPosts, invariant } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
|
||||||
|
export async function getStaticPaths() {
|
||||||
|
const [posts, pages, settings] = await Promise.all([getAllPosts(), await getAllPages(), await getSettings()]);
|
||||||
|
const allPosts = [...posts, ...pages];
|
||||||
|
return allPosts.map((post) => ({
|
||||||
|
params: { slug: post.slug },
|
||||||
|
props: { post, posts, settings },
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Props = InferGetStaticPropsType<typeof getStaticPaths>;
|
||||||
|
|
||||||
|
const {post, posts, settings} = Astro.props as Props;
|
||||||
|
invariant(settings, "Settings are required");
|
||||||
|
const postClass = post.tags?.map((tag) => "tag-" + tag.slug).join(" ");
|
||||||
|
const bodyClass = `post-template ${postClass}`;
|
||||||
|
---
|
||||||
|
|
||||||
|
<DefaultPageLayout
|
||||||
|
content={{ title: post.title, description: post.excerpt }}
|
||||||
|
settings={settings}
|
||||||
|
bodyClass={bodyClass}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
post.primary_author ? (
|
||||||
|
<Post
|
||||||
|
post={post}
|
||||||
|
settings={settings}
|
||||||
|
postClass={postClass}
|
||||||
|
posts={posts}
|
||||||
|
/>
|
||||||
|
|
||||||
|
) : (
|
||||||
|
<Page page={post} settings={settings} pageClass={postClass} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</DefaultPageLayout>
|
||||||
|
|
||||||
|
<style lang="scss"></style>
|
|
@ -0,0 +1,49 @@
|
||||||
|
---
|
||||||
|
import type { GetStaticPathsOptions, Page } from 'astro';
|
||||||
|
import DefaultPageLayout from "../../layouts/default.astro";
|
||||||
|
import PostPreviewList from "../../components/PostPreviewList.astro";
|
||||||
|
import HeroContent from "../../components/HeroContent.astro";
|
||||||
|
import Paginator from "../../components/Paginator.astro";
|
||||||
|
import { getSettings, getAllPosts, invariant, type Post } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
|
||||||
|
export async function getStaticPaths({ paginate }:GetStaticPathsOptions) {
|
||||||
|
const posts = await getAllPosts();
|
||||||
|
return paginate(posts, {
|
||||||
|
pageSize: 5,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Props = {
|
||||||
|
page: Page<Post>
|
||||||
|
};
|
||||||
|
|
||||||
|
const settings = await getSettings();
|
||||||
|
invariant(settings, "Settings are required");
|
||||||
|
|
||||||
|
const title = settings.title;
|
||||||
|
const description = settings.description;
|
||||||
|
const { page } = Astro.props as Props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<DefaultPageLayout
|
||||||
|
content={{ title, description }}
|
||||||
|
settings={settings}
|
||||||
|
bodyClass={"home-template"}
|
||||||
|
>
|
||||||
|
<HeroContent
|
||||||
|
mainTitle={"Archives"}
|
||||||
|
description={"All the posts"}
|
||||||
|
featureImg={settings.cover_image || ""}
|
||||||
|
settings={settings}
|
||||||
|
addClass={"hero-cta bg-gradient"}
|
||||||
|
>
|
||||||
|
<h1 class="site-title" slot="title">Archives</h1>
|
||||||
|
</HeroContent>
|
||||||
|
|
||||||
|
<main id="site-main" class="site-main outer">
|
||||||
|
<div class="inner posts">
|
||||||
|
<PostPreviewList posts={page.data} settings={settings} />
|
||||||
|
<Paginator {page} />
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</DefaultPageLayout>
|
|
@ -0,0 +1,120 @@
|
||||||
|
---
|
||||||
|
import type { InferGetStaticParamsType, InferGetStaticPropsType } from 'astro';
|
||||||
|
import DefaultPageLayout from "../../layouts/default.astro";
|
||||||
|
import PostPreviewList from "../../components/PostPreviewList.astro";
|
||||||
|
import { getAllPosts, getAllAuthors, getSettings, twitter, facebook, invariant } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
|
||||||
|
export async function getStaticPaths() {
|
||||||
|
const posts = await getAllPosts();
|
||||||
|
const { authors } = await getAllAuthors();
|
||||||
|
const settings = await getSettings();
|
||||||
|
|
||||||
|
return authors.map((author) => {
|
||||||
|
const filteredPosts = posts.filter((post) =>
|
||||||
|
post.authors?.map((author) => author.slug).includes(author.slug)
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
params: { slug: author.slug },
|
||||||
|
props: {
|
||||||
|
posts: filteredPosts,
|
||||||
|
settings,
|
||||||
|
author,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Params = InferGetStaticParamsType<typeof getStaticPaths>;
|
||||||
|
export type Props = InferGetStaticPropsType<typeof getStaticPaths>;
|
||||||
|
|
||||||
|
const { posts, settings, author } = Astro.props;
|
||||||
|
invariant(settings, "Settings are required");
|
||||||
|
const title = `Posts by author: ${author.name}`;
|
||||||
|
const description = `All of the articles we've posted and linked so far under the author: ${author.name}`;
|
||||||
|
---
|
||||||
|
|
||||||
|
<DefaultPageLayout
|
||||||
|
bodyClass={`author-template author-${author.slug}`}
|
||||||
|
content={{ title, description }}
|
||||||
|
settings={settings}
|
||||||
|
>
|
||||||
|
<section class="outer author-template">
|
||||||
|
<div class="inner posts">
|
||||||
|
<header class="author-profile">
|
||||||
|
<div class="author-profile-content">
|
||||||
|
{author.profile_image ? (
|
||||||
|
<img
|
||||||
|
class="author-profile-pic"
|
||||||
|
src={author.profile_image}
|
||||||
|
alt={author.name}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span class="author-profile-pic">
|
||||||
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
<path
|
||||||
|
d="M3.513 18.998C4.749 15.504 8.082 13 12 13s7.251 2.504 8.487 5.998C18.47 21.442 15.417 23 12 23s-6.47-1.558-8.487-4.002zM12 12c2.21 0 4-2.79 4-5s-1.79-4-4-4-4 1.79-4 4 1.79 5 4 5z"
|
||||||
|
fill="#FFF"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<h1>{author.name}</h1>
|
||||||
|
<p>
|
||||||
|
{author.bio
|
||||||
|
? author.bio
|
||||||
|
: author.count?.posts || 0 > 0
|
||||||
|
? `${author.count?.posts} Posts`
|
||||||
|
: "No posts"}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="author-profile-meta">
|
||||||
|
{author.location && (
|
||||||
|
<div class="author-profile-location">📍 {author.location}</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{author.website && (
|
||||||
|
<span>
|
||||||
|
<a
|
||||||
|
class="author-profile-social-link"
|
||||||
|
href={author.website}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>
|
||||||
|
{author.website}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{author.twitter && (
|
||||||
|
<span>
|
||||||
|
<a
|
||||||
|
class="author-profile-social-link"
|
||||||
|
href={twitter(author.twitter)}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>
|
||||||
|
{author.twitter}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{author.facebook && (
|
||||||
|
<span>
|
||||||
|
<a
|
||||||
|
class="author-profile-social-link"
|
||||||
|
href={facebook(author.facebook)}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>
|
||||||
|
{author.facebook}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<PostPreviewList posts={posts} settings={settings} />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</DefaultPageLayout>
|
|
@ -0,0 +1,43 @@
|
||||||
|
---
|
||||||
|
import DefaultPageLayout from "../layouts/default.astro";
|
||||||
|
import AuthorCard from "../components/AuthorCard.astro";
|
||||||
|
import { getAllAuthors, getSettings, invariant } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
|
||||||
|
let title = "All Authors";
|
||||||
|
let description = "All the authors";
|
||||||
|
const { authors } = await getAllAuthors();
|
||||||
|
const settings = await getSettings();
|
||||||
|
invariant(settings, 'Settings not found');
|
||||||
|
---
|
||||||
|
|
||||||
|
<DefaultPageLayout content={{ title, description }} settings={settings}>
|
||||||
|
<section class="outer">
|
||||||
|
<div class="inner posts">
|
||||||
|
<h1>
|
||||||
|
{settings.title}
|
||||||
|
</h1>
|
||||||
|
<div class="page__excerpt m-t text-acc-3 text-center text-lg">
|
||||||
|
Collection of Tags
|
||||||
|
</div>
|
||||||
|
<div class="author-feed">
|
||||||
|
{authors.map((author) => (
|
||||||
|
<article class="post-card ">
|
||||||
|
<AuthorCard author={author} settings={settings} />
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</DefaultPageLayout>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.author-feed {
|
||||||
|
margin: 0 auto;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
grid-auto-rows: 1fr;
|
||||||
|
grid-column-gap: 9vh;
|
||||||
|
grid-row-gap: 10vh;
|
||||||
|
padding: 4vmin 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,31 @@
|
||||||
|
---
|
||||||
|
import DefaultPageLayout from "../layouts/default.astro";
|
||||||
|
import PostPreviewList from "../components/PostPreviewList.astro";
|
||||||
|
import HeroContent from "../components/HeroContent.astro";
|
||||||
|
import { getPosts, getSettings, invariant } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
const { posts } = await getPosts();
|
||||||
|
const settings = await getSettings();
|
||||||
|
invariant(settings, 'Settings not found');
|
||||||
|
const title = settings.title;
|
||||||
|
const description = settings.description;
|
||||||
|
---
|
||||||
|
|
||||||
|
<DefaultPageLayout
|
||||||
|
content={{ title, description }}
|
||||||
|
settings={settings}
|
||||||
|
bodyClass={"home-template"}
|
||||||
|
>
|
||||||
|
<HeroContent
|
||||||
|
mainTitle={settings.title}
|
||||||
|
description={settings.description}
|
||||||
|
featureImg={settings?.cover_image || ""}
|
||||||
|
settings={settings}
|
||||||
|
addClass={"hero-cta bg-gradient"}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<main id="site-main" class="site-main outer">
|
||||||
|
<div class="inner posts">
|
||||||
|
<PostPreviewList posts={posts} settings={settings} isHome={true} />
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</DefaultPageLayout>
|
|
@ -0,0 +1,101 @@
|
||||||
|
---
|
||||||
|
import type { InferGetStaticParamsType, InferGetStaticPropsType } from 'astro';
|
||||||
|
import DefaultPageLayout from "../../layouts/default.astro";
|
||||||
|
import PostPreview from "../../components/PostPreview.astro";
|
||||||
|
import { getAllPosts, getAllTags, getSettings, invariant } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
import { getGhostImgPath } from "../../utils";
|
||||||
|
|
||||||
|
export async function getStaticPaths() {
|
||||||
|
const posts = await getAllPosts();
|
||||||
|
const { tags } = await getAllTags();
|
||||||
|
const settings = await getSettings();
|
||||||
|
|
||||||
|
return tags.map((tag) => {
|
||||||
|
const filteredPosts = posts.filter((post) =>
|
||||||
|
post.tags?.map((tag) => tag.slug).includes(tag.slug)
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
params: { slug: tag.slug },
|
||||||
|
props: {
|
||||||
|
posts: filteredPosts,
|
||||||
|
settings,
|
||||||
|
tag,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Params = InferGetStaticParamsType<typeof getStaticPaths>;
|
||||||
|
export type Props = InferGetStaticPropsType<typeof getStaticPaths>;
|
||||||
|
|
||||||
|
const { posts, settings, tag } = Astro.props;
|
||||||
|
invariant(settings, 'Settings not found');
|
||||||
|
const title = `Posts by Tag: ${tag.name}`;
|
||||||
|
const description = `all of the articles we've posted and linked so far under the tag: ${tag.name}`;
|
||||||
|
---
|
||||||
|
|
||||||
|
<DefaultPageLayout
|
||||||
|
bodyClass={`tag-template tag-${tag.slug}`}
|
||||||
|
content={{ title, description }}
|
||||||
|
settings={settings}
|
||||||
|
>
|
||||||
|
<main id="site-main" class="site-main outer">
|
||||||
|
<div class="inner posts">
|
||||||
|
<div class="post-feed">
|
||||||
|
<section class="post-card post-card-large">
|
||||||
|
{tag.feature_image && (
|
||||||
|
<div class="post-card-image-link">
|
||||||
|
<img
|
||||||
|
class="post-card-image"
|
||||||
|
srcset={`${getGhostImgPath(
|
||||||
|
settings.url,
|
||||||
|
tag.feature_image,
|
||||||
|
300
|
||||||
|
)} 300w,
|
||||||
|
${getGhostImgPath(
|
||||||
|
settings.url,
|
||||||
|
tag.feature_image,
|
||||||
|
600
|
||||||
|
)} 600w,
|
||||||
|
${getGhostImgPath(
|
||||||
|
settings.url,
|
||||||
|
tag.feature_image,
|
||||||
|
1000
|
||||||
|
)} 1000w,
|
||||||
|
${getGhostImgPath(
|
||||||
|
settings.url,
|
||||||
|
tag.feature_image,
|
||||||
|
2000
|
||||||
|
)} 2000w`}
|
||||||
|
sizes="(max-width: 1000px) 400px, 800px"
|
||||||
|
src={getGhostImgPath(settings.url, tag.feature_image, 600)}
|
||||||
|
alt={tag.name}
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div class="post-card-content">
|
||||||
|
<div class="post-card-content-link">
|
||||||
|
<header class="post-card-header">
|
||||||
|
<div class="post-card-primary-tag">Tagged</div>
|
||||||
|
<h2 class="post-card-title">{tag.name}</h2>
|
||||||
|
</header>
|
||||||
|
<div class="post-card-excerpt">
|
||||||
|
<p>
|
||||||
|
{tag.description
|
||||||
|
? tag.description
|
||||||
|
: `A collection of ${tag.count?.posts || 0 } Post${
|
||||||
|
tag.count?.posts ?? 0 > 1 ? "s" : ""
|
||||||
|
}`}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{posts.map((post, index) => (
|
||||||
|
<PostPreview post={post} index={index} settings={settings} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</DefaultPageLayout>
|
|
@ -0,0 +1,49 @@
|
||||||
|
---
|
||||||
|
import DefaultPageLayout from "../layouts/default.astro";
|
||||||
|
import TagCard from "../components/TagCard.astro";
|
||||||
|
import { getSettings, getAllTags, invariant } from "@matthiesenxyz/astro-ghostcms/api";
|
||||||
|
|
||||||
|
|
||||||
|
let title = "All Tags";
|
||||||
|
let description = "All the tags used so far...";
|
||||||
|
|
||||||
|
const { tags } = await getAllTags();
|
||||||
|
const settings = await getSettings();
|
||||||
|
invariant(settings, "Settings not found");
|
||||||
|
---
|
||||||
|
|
||||||
|
<DefaultPageLayout content={{ title, description }} settings={settings}>
|
||||||
|
<section class="outer">
|
||||||
|
<div class="inner posts">
|
||||||
|
<h1>
|
||||||
|
{settings.title}
|
||||||
|
</h1>
|
||||||
|
<div class="page__excerpt m-t text-acc-3 text-center text-lg">
|
||||||
|
Collection of Tags
|
||||||
|
</div>
|
||||||
|
<div class="tag-feed">
|
||||||
|
{
|
||||||
|
tags
|
||||||
|
.filter((tag) => tag.slug && !tag.slug.startsWith("hash-"))
|
||||||
|
.map((tag) => (
|
||||||
|
<article class="post-card ">
|
||||||
|
<TagCard tag={tag} settings={settings} />
|
||||||
|
</article>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</DefaultPageLayout>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.tag-feed {
|
||||||
|
margin: 0 auto;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
grid-auto-rows: 1fr;
|
||||||
|
grid-column-gap: 9vh;
|
||||||
|
grid-row-gap: 10vh;
|
||||||
|
padding: 4vmin 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,395 @@
|
||||||
|
/* Reset
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
@import "./reset";
|
||||||
|
@import "./variables";
|
||||||
|
|
||||||
|
/* 1. Global - Set up the things
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
/* Import CSS reset and base styles */
|
||||||
|
:root {
|
||||||
|
/* Colours */
|
||||||
|
--color-green: #a4d037;
|
||||||
|
--color-yellow: #fecd35;
|
||||||
|
--color-red: #f05230;
|
||||||
|
--color-darkgrey: #15171a;
|
||||||
|
--color-midgrey: #738a94;
|
||||||
|
--color-lightgrey: #c5d2d9;
|
||||||
|
--color-wash: #e5eff5;
|
||||||
|
--color-darkmode: #151719;
|
||||||
|
|
||||||
|
/*
|
||||||
|
An accent color is also set by Ghost itself in
|
||||||
|
Ghost Admin > Settings > Brand
|
||||||
|
|
||||||
|
--ghost-accent-color: {value};
|
||||||
|
|
||||||
|
You can use this variale throughout your styles
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Fonts */
|
||||||
|
--font-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
|
||||||
|
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||||
|
sans-serif;
|
||||||
|
--font-serif: Georgia, Times, serif;
|
||||||
|
--font-mono: Menlo, Courier, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. Layout - Page building blocks
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
.viewport {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-content {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Full width page blocks */
|
||||||
|
.outer {
|
||||||
|
position: relative;
|
||||||
|
padding: 0 4vmin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Centered content container blocks */
|
||||||
|
.inner {
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 1200px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 9. Error Template
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
.error-content {
|
||||||
|
padding: 14vw 4vw 6vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
padding-bottom: 10vw;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-code {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--ghost-accent-color);
|
||||||
|
font-size: 12vw;
|
||||||
|
line-height: 1em;
|
||||||
|
letter-spacing: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-description {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--color-midgrey);
|
||||||
|
font-size: 3.2rem;
|
||||||
|
line-height: 1.3em;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-link {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 940px) {
|
||||||
|
.error-content .post-card {
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
.error-content {
|
||||||
|
padding-top: 24vw;
|
||||||
|
}
|
||||||
|
.error-code {
|
||||||
|
font-size: 11.2rem;
|
||||||
|
}
|
||||||
|
.error-message {
|
||||||
|
padding-bottom: 16vw;
|
||||||
|
}
|
||||||
|
.error-description {
|
||||||
|
margin: 5px 0 0 0;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
.error-content {
|
||||||
|
padding-top: 28vw;
|
||||||
|
}
|
||||||
|
.error-message {
|
||||||
|
padding-bottom: 14vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.author-template .posts {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 200px 1fr 1fr;
|
||||||
|
grid-gap: 4vmin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author-template .posts .post-feed {
|
||||||
|
grid-column: 2 / 4;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.author-template .posts .post-feed {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 650px) {
|
||||||
|
.author-template .posts {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
grid-gap: 0;
|
||||||
|
}
|
||||||
|
.author-template .posts .post-feed {
|
||||||
|
grid-column: 1 / auto;
|
||||||
|
}
|
||||||
|
.author-profile {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
.author-profile-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
padding-right: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 11. Site Footer
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* 12. Dark Mode
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* If you prefer a dark color scheme, you can enable dark mode
|
||||||
|
by adding the following code to the Head section of "Code Injection"
|
||||||
|
settings inside: Ghost Admin > Settings > Advanced
|
||||||
|
|
||||||
|
<script>document.documentElement.classList.add('dark-mode');</script>
|
||||||
|
|
||||||
|
Or you can just edit default.hbs and add the .dark-mode class directly
|
||||||
|
to the html tag on the very first line of the file.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
html.dark-mode body {
|
||||||
|
color: rgba(255, 255, 255, 0.75);
|
||||||
|
background: var(--color-darkmode);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode img {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .post-card,
|
||||||
|
html.dark-mode .post-card:hover {
|
||||||
|
border-bottom-color: color-mod(var(--color-darkmode) l(+8%));
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .post-card-byline-content a {
|
||||||
|
color: rgba(255, 255, 255, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .post-card-byline-content a:hover {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .post-card-image {
|
||||||
|
background: var(--color-darkmode);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .post-card-title {
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .post-card-excerpt {
|
||||||
|
color: var(--color-midgrey);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .post-full-content {
|
||||||
|
background: var(--color-darkmode);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .article-title {
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .article-excerpt {
|
||||||
|
color: color-mod(var(--color-midgrey) l(+10%));
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .post-full-image {
|
||||||
|
background-color: color-mod(var(--color-darkmode) l(+8%));
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .article-byline {
|
||||||
|
border-top-color: color-mod(var(--color-darkmode) l(+15%));
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .article-byline-meta h4 a {
|
||||||
|
color: rgba(255, 255, 255, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .article-byline-meta h4 a:hover {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .no-image .author-social-link a {
|
||||||
|
color: rgba(255, 255, 255, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .gh-content h1,
|
||||||
|
html.dark-mode .gh-content h2,
|
||||||
|
html.dark-mode .gh-content h3,
|
||||||
|
html.dark-mode .gh-content h4,
|
||||||
|
html.dark-mode .gh-content h5,
|
||||||
|
html.dark-mode .gh-content h6 {
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .gh-content pre {
|
||||||
|
background: color-mod(var(--color-darkgrey) l(-8%));
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .gh-content :not(pre) > code {
|
||||||
|
background: color-mod(var(--color-darkgrey) l(+6%));
|
||||||
|
border-color: color-mod(var(--color-darkmode) l(+8%));
|
||||||
|
color: var(--color-wash);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .post-full-content a {
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: inset 0 -1px 0 #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .post-full-content strong {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .post-full-content em {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .post-full-content code {
|
||||||
|
color: #fff;
|
||||||
|
background: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode hr {
|
||||||
|
border-top-color: color-mod(var(--color-darkmode) l(+8%));
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .post-full-content hr:after {
|
||||||
|
background: color-mod(var(--color-darkmode) l(+8%));
|
||||||
|
box-shadow: var(--color-darkmode) 0 0 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .gh-content figcaption {
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .post-full-content table td:first-child {
|
||||||
|
background-image: linear-gradient(
|
||||||
|
to right,
|
||||||
|
var(--color-darkmode) 50%,
|
||||||
|
color-mod(var(--color-darkmode) a(0%)) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .post-full-content table td:last-child {
|
||||||
|
background-image: linear-gradient(
|
||||||
|
to left,
|
||||||
|
var(--color-darkmode) 50%,
|
||||||
|
color-mod(var(--color-darkmode) a(0%)) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .post-full-content table th {
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
background-color: color-mod(var(--color-darkmode) l(+8%));
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .post-full-content table th,
|
||||||
|
html.dark-mode .post-full-content table td {
|
||||||
|
border: color-mod(var(--color-darkmode) l(+8%)) 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .post-full-content .kg-bookmark-container,
|
||||||
|
html.dark-mode .post-full-content .kg-bookmark-container:hover {
|
||||||
|
color: rgba(255, 255, 255, 0.75);
|
||||||
|
box-shadow: 0 0 1px rgba(255, 255, 255, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .post-full-content input {
|
||||||
|
color: color-mod(var(--color-midgrey) l(-30%));
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .kg-bookmark-title {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .kg-bookmark-description {
|
||||||
|
color: rgba(255, 255, 255, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .kg-bookmark-metadata {
|
||||||
|
color: rgba(255, 255, 255, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .site-archive-header .no-image {
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
background: var(--color-darkmode);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .subscribe-form {
|
||||||
|
border: none;
|
||||||
|
background: linear-gradient(
|
||||||
|
color-mod(var(--color-darkmode) l(-6%)),
|
||||||
|
color-mod(var(--color-darkmode) l(-3%))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .subscribe-form-title {
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .subscribe-form p {
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .subscribe-email {
|
||||||
|
border-color: color-mod(var(--color-darkmode) l(+6%));
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
background: color-mod(var(--color-darkmode) l(+3%));
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .subscribe-email:focus {
|
||||||
|
border-color: color-mod(var(--color-darkmode) l(+25%));
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .subscribe-form button {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .subscribe-form .invalid .message-error,
|
||||||
|
html.dark-mode .subscribe-form .error .message-error {
|
||||||
|
color: color-mod(var(--color-red) l(+5%) s(-5%));
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode .subscribe-form .success .message-success {
|
||||||
|
color: color-mod(var(--color-green) l(+5%) s(-5%));
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark-mode figcaption {
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
|
@ -0,0 +1,464 @@
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
div,
|
||||||
|
span,
|
||||||
|
applet,
|
||||||
|
object,
|
||||||
|
iframe,
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6,
|
||||||
|
p,
|
||||||
|
blockquote,
|
||||||
|
pre,
|
||||||
|
a,
|
||||||
|
abbr,
|
||||||
|
acronym,
|
||||||
|
address,
|
||||||
|
big,
|
||||||
|
cite,
|
||||||
|
code,
|
||||||
|
del,
|
||||||
|
dfn,
|
||||||
|
em,
|
||||||
|
img,
|
||||||
|
ins,
|
||||||
|
kbd,
|
||||||
|
q,
|
||||||
|
s,
|
||||||
|
samp,
|
||||||
|
small,
|
||||||
|
strike,
|
||||||
|
strong,
|
||||||
|
sub,
|
||||||
|
sup,
|
||||||
|
tt,
|
||||||
|
var,
|
||||||
|
dl,
|
||||||
|
dt,
|
||||||
|
dd,
|
||||||
|
ol,
|
||||||
|
ul,
|
||||||
|
li,
|
||||||
|
fieldset,
|
||||||
|
form,
|
||||||
|
label,
|
||||||
|
legend,
|
||||||
|
table,
|
||||||
|
caption,
|
||||||
|
tbody,
|
||||||
|
tfoot,
|
||||||
|
thead,
|
||||||
|
tr,
|
||||||
|
th,
|
||||||
|
td,
|
||||||
|
article,
|
||||||
|
aside,
|
||||||
|
canvas,
|
||||||
|
details,
|
||||||
|
embed,
|
||||||
|
figure,
|
||||||
|
figcaption,
|
||||||
|
footer,
|
||||||
|
header,
|
||||||
|
hgroup,
|
||||||
|
menu,
|
||||||
|
nav,
|
||||||
|
output,
|
||||||
|
ruby,
|
||||||
|
section,
|
||||||
|
summary,
|
||||||
|
time,
|
||||||
|
mark,
|
||||||
|
audio,
|
||||||
|
video {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
font: inherit;
|
||||||
|
font-size: 100%;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
ol,
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
blockquote,
|
||||||
|
q {
|
||||||
|
quotes: none;
|
||||||
|
}
|
||||||
|
blockquote:before,
|
||||||
|
blockquote:after,
|
||||||
|
q:before,
|
||||||
|
q:after {
|
||||||
|
content: "";
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border-spacing: 0;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
html {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: sans-serif;
|
||||||
|
|
||||||
|
-ms-text-size-adjust: 100%;
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
}
|
||||||
|
*,
|
||||||
|
*:before,
|
||||||
|
*:after {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
a:active,
|
||||||
|
a:hover {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
i,
|
||||||
|
em,
|
||||||
|
dfn {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
margin: 0.67em 0;
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
position: relative;
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
svg:not(:root) {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
mark {
|
||||||
|
background-color: #fdffb6;
|
||||||
|
}
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
pre,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
margin: 0; /* 3 */
|
||||||
|
color: inherit; /* 1 */
|
||||||
|
font: inherit; /* 2 */
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
overflow: visible;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
button,
|
||||||
|
html input[type="button"],
|
||||||
|
/* 1 */
|
||||||
|
input[type="reset"],
|
||||||
|
input[type="submit"] {
|
||||||
|
cursor: pointer; /* 3 */
|
||||||
|
|
||||||
|
-webkit-appearance: button; /* 2 */
|
||||||
|
}
|
||||||
|
button[disabled],
|
||||||
|
html input[disabled] {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
input::-moz-focus-inner {
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
input:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
input[type="checkbox"],
|
||||||
|
input[type="radio"] {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
padding: 0; /* 2 */
|
||||||
|
}
|
||||||
|
input[type="number"]::-webkit-inner-spin-button,
|
||||||
|
input[type="number"]::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
input[type="search"] {
|
||||||
|
box-sizing: content-box; /* 2 */
|
||||||
|
|
||||||
|
-webkit-appearance: textfield; /* 1 */
|
||||||
|
}
|
||||||
|
input[type="search"]::-webkit-search-cancel-button,
|
||||||
|
input[type="search"]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
legend {
|
||||||
|
padding: 0; /* 2 */
|
||||||
|
border: 0; /* 1 */
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border-spacing: 0;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
Base styles: opinionated defaults
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 62.5%;
|
||||||
|
|
||||||
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
color: #35373a;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
|
||||||
|
Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
line-height: 1.6em;
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
letter-spacing: 0;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-moz-font-feature-settings: "liga" on;
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
text-shadow: none;
|
||||||
|
background: #daf2fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
margin: 2.5em 0 3.5em;
|
||||||
|
padding: 0;
|
||||||
|
height: 1px;
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
audio,
|
||||||
|
canvas,
|
||||||
|
iframe,
|
||||||
|
img,
|
||||||
|
svg,
|
||||||
|
video {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
::not(.gh-content) p,
|
||||||
|
::not(.gh-content) ul,
|
||||||
|
::not(.gh-content) ol,
|
||||||
|
::not(.gh-content) dl,
|
||||||
|
::not(.gh-content) blockquote {
|
||||||
|
margin: 0 0 1.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol,
|
||||||
|
ul {
|
||||||
|
padding-left: 1.3em;
|
||||||
|
padding-right: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol ol,
|
||||||
|
ul ul,
|
||||||
|
ul ol,
|
||||||
|
ol ul {
|
||||||
|
margin: 0.5em 0 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol {
|
||||||
|
list-style: decimal;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
padding-left: 0.3em;
|
||||||
|
line-height: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
li + li {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
float: left;
|
||||||
|
margin: 0 20px 0 0;
|
||||||
|
width: 120px;
|
||||||
|
color: #daf2fd;
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
margin: 0 0 5px 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
margin: 1.5em 0;
|
||||||
|
padding: 0 1.6em 0 1.6em;
|
||||||
|
border-left: #daf2fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote p {
|
||||||
|
margin: 0.8em 0;
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote small {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0.8em 0 0.8em 1.5em;
|
||||||
|
font-size: 0.9em;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
/* Quotation marks */
|
||||||
|
blockquote small:before {
|
||||||
|
content: "\2014 \00A0";
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote cite {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
blockquote cite a {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #15171a;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
margin-top: 0;
|
||||||
|
line-height: 1.15;
|
||||||
|
font-weight: 600;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: 0 0 0.5em 0;
|
||||||
|
font-size: 4.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: -0.015em;
|
||||||
|
}
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
h1 {
|
||||||
|
font-size: 2.8rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin: 1.5em 0 0.5em 0;
|
||||||
|
font-size: 2.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
h2 {
|
||||||
|
font-size: 2.3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 1.5em 0 0.5em 0;
|
||||||
|
font-size: 2.4rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
h3 {
|
||||||
|
font-size: 1.7rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin: 1.5em 0 0.5em 0;
|
||||||
|
font-size: 2.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
margin: 1.5em 0 0.5em 0;
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
margin: 1.5em 0 0.5em 0;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
$color-green: #a4d037;
|
||||||
|
$color-yellow: #fecd35;
|
||||||
|
$color-red: #f05230;
|
||||||
|
$color-darkgrey: #15171a;
|
||||||
|
$color-midgrey: #738a94;
|
||||||
|
$color-lightgrey: #c5d2d9;
|
||||||
|
$color-wash: #e5eff5;
|
||||||
|
$color-darkmode: #151719;
|
|
@ -0,0 +1,32 @@
|
||||||
|
export const getGhostImgPath = (
|
||||||
|
baseUrl: string,
|
||||||
|
imgUrl: string,
|
||||||
|
width = 0
|
||||||
|
): string => {
|
||||||
|
if (!imgUrl) return "";
|
||||||
|
if (!imgUrl.startsWith(baseUrl)) {
|
||||||
|
return imgUrl;
|
||||||
|
}
|
||||||
|
const relativePath = imgUrl.substring(`${baseUrl}content/images`.length);
|
||||||
|
const cleanedBaseUrl = baseUrl.replace(/\/~/, "");
|
||||||
|
if (width && width > 0) {
|
||||||
|
return `${cleanedBaseUrl}content/images/size/w${width}/${relativePath}`;
|
||||||
|
}
|
||||||
|
return `${cleanedBaseUrl}content/images/${width}${relativePath}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const truncate = (input: string, size: number): string =>
|
||||||
|
input.length > size ? `${input.substring(0, size)}...` : input;
|
||||||
|
|
||||||
|
export const formatDate = (dateInput: string): string => {
|
||||||
|
const dateObject = new Date(dateInput);
|
||||||
|
return dateObject.toDateString();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const uniqWith = <T>(
|
||||||
|
arr: Array<T>,
|
||||||
|
fn: (element: T, step: T) => number
|
||||||
|
): Array<T> =>
|
||||||
|
arr.filter(
|
||||||
|
(element, index) => arr.findIndex((step) => fn(element, step)) === index
|
||||||
|
);
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "astro/tsconfigs/strict"
|
||||||
|
}
|
Loading…
Reference in New Issue