feat: style editor for blog
This commit is contained in:
@@ -5,6 +5,7 @@ import { app } from 'electron';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { getDatabase } from '../database';
|
||||
import { posts, projects } from '../database/schema';
|
||||
import { sanitizePicoTheme, type PicoThemeName } from '../shared/picoThemes';
|
||||
import {
|
||||
normalizeTaxonomyTerm,
|
||||
normalizeNonEmptyTaxonomyTerm,
|
||||
@@ -22,6 +23,7 @@ export interface ProjectMetadata {
|
||||
mainLanguage?: string; // Main language for AI-generated content (ISO code, e.g., 'en', 'de', 'es')
|
||||
defaultAuthor?: string; // Default author for new posts and media
|
||||
maxPostsPerPage?: number; // Preview/server maximum posts per page (default 50)
|
||||
picoTheme?: PicoThemeName; // Selected Pico CSS theme for preview/rendering
|
||||
}
|
||||
|
||||
const DEFAULT_MAX_POSTS_PER_PAGE = 50;
|
||||
@@ -61,10 +63,12 @@ function sanitizePublicUrl(value: unknown): string | undefined {
|
||||
function normalizeProjectMetadata(metadata: ProjectMetadata): ProjectMetadata {
|
||||
const maxPostsPerPage = sanitizeMaxPostsPerPage(metadata.maxPostsPerPage);
|
||||
const publicUrl = sanitizePublicUrl(metadata.publicUrl);
|
||||
const picoTheme = sanitizePicoTheme(metadata.picoTheme);
|
||||
return {
|
||||
...metadata,
|
||||
publicUrl,
|
||||
maxPostsPerPage,
|
||||
picoTheme,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -179,6 +183,9 @@ export class MetaEngine extends EventEmitter {
|
||||
if (updates.maxPostsPerPage !== undefined) {
|
||||
normalizedUpdates.maxPostsPerPage = sanitizeMaxPostsPerPage(updates.maxPostsPerPage);
|
||||
}
|
||||
if (updates.picoTheme !== undefined) {
|
||||
normalizedUpdates.picoTheme = sanitizePicoTheme(updates.picoTheme);
|
||||
}
|
||||
|
||||
if (!this.projectMetadata) {
|
||||
this.projectMetadata = normalizeProjectMetadata({
|
||||
@@ -189,6 +196,7 @@ export class MetaEngine extends EventEmitter {
|
||||
mainLanguage: normalizedUpdates.mainLanguage,
|
||||
defaultAuthor: normalizedUpdates.defaultAuthor,
|
||||
maxPostsPerPage: normalizedUpdates.maxPostsPerPage,
|
||||
picoTheme: normalizedUpdates.picoTheme,
|
||||
});
|
||||
} else {
|
||||
this.projectMetadata = normalizeProjectMetadata({
|
||||
|
||||
@@ -3,6 +3,7 @@ import { marked } from 'marked';
|
||||
import { Liquid } from 'liquidjs';
|
||||
import type { MediaData } from './MediaEngine';
|
||||
import type { PostData } from './PostEngine';
|
||||
import { PICO_THEME_NAMES } from '../shared/picoThemes';
|
||||
|
||||
export interface HtmlRewriteContext {
|
||||
canonicalPostPathBySlug: Map<string, string>;
|
||||
@@ -41,6 +42,8 @@ export type DateArchiveContext = {
|
||||
export interface PostListTemplateContext {
|
||||
page_title: string;
|
||||
language: string;
|
||||
pico_stylesheet_href?: string;
|
||||
html_theme_attribute?: string;
|
||||
is_date_archive: boolean;
|
||||
show_archive_range_heading: boolean;
|
||||
archive_context: {
|
||||
@@ -67,6 +70,8 @@ export interface PostListTemplateContext {
|
||||
export interface SinglePostTemplateContext {
|
||||
page_title: string;
|
||||
language: string;
|
||||
pico_stylesheet_href?: string;
|
||||
html_theme_attribute?: string;
|
||||
post: TemplatePostEntry;
|
||||
canonical_post_path_by_slug: Record<string, string>;
|
||||
canonical_media_path_by_source_path: Record<string, string>;
|
||||
@@ -75,6 +80,8 @@ export interface SinglePostTemplateContext {
|
||||
export interface NotFoundTemplateContext {
|
||||
page_title: string;
|
||||
language: string;
|
||||
pico_stylesheet_href?: string;
|
||||
html_theme_attribute?: string;
|
||||
}
|
||||
|
||||
export interface RoutePagination {
|
||||
@@ -96,11 +103,20 @@ export interface PostEngineContract {
|
||||
getPost: (id: string) => Promise<PostData | null>;
|
||||
}
|
||||
|
||||
export const PREVIEW_ASSETS = {
|
||||
export const PREVIEW_ASSETS: Record<string, { modulePath: string; contentType: string }> = {
|
||||
'pico.min.css': {
|
||||
modulePath: '@picocss/pico/css/pico.min.css',
|
||||
contentType: 'text/css; charset=utf-8',
|
||||
},
|
||||
...Object.fromEntries(
|
||||
PICO_THEME_NAMES.map((theme) => [
|
||||
`pico.${theme}.min.css`,
|
||||
{
|
||||
modulePath: `@picocss/pico/css/pico.${theme}.min.css`,
|
||||
contentType: 'text/css; charset=utf-8',
|
||||
},
|
||||
])
|
||||
),
|
||||
'lightbox.min.css': {
|
||||
modulePath: 'lightbox2/dist/css/lightbox.min.css',
|
||||
contentType: 'text/css; charset=utf-8',
|
||||
@@ -109,7 +125,7 @@ export const PREVIEW_ASSETS = {
|
||||
modulePath: 'lightbox2/dist/js/lightbox-plus-jquery.min.js',
|
||||
contentType: 'application/javascript; charset=utf-8',
|
||||
},
|
||||
} as const;
|
||||
};
|
||||
|
||||
export const PREVIEW_IMAGE_ASSETS = {
|
||||
'prev.png': {
|
||||
@@ -616,6 +632,8 @@ export class PageRenderer {
|
||||
basePathname: string;
|
||||
page_title: string;
|
||||
language: string;
|
||||
pico_stylesheet_href?: string;
|
||||
html_theme_attribute?: string;
|
||||
pagination?: PaginationContext;
|
||||
},
|
||||
): PostListTemplateContext {
|
||||
@@ -710,6 +728,8 @@ export class PageRenderer {
|
||||
return {
|
||||
page_title: options.page_title,
|
||||
language: options.language,
|
||||
pico_stylesheet_href: options.pico_stylesheet_href,
|
||||
html_theme_attribute: options.html_theme_attribute,
|
||||
is_date_archive: options.routeKind === 'date',
|
||||
show_archive_range_heading: hasRangeHeading,
|
||||
archive_context: options.routeKind === 'date'
|
||||
@@ -763,6 +783,8 @@ export class PageRenderer {
|
||||
basePathname: string;
|
||||
page_title: string;
|
||||
language: string;
|
||||
pico_stylesheet_href?: string;
|
||||
html_theme_attribute?: string;
|
||||
pagination?: PaginationContext;
|
||||
},
|
||||
postEngine?: PostEngineContract,
|
||||
@@ -786,7 +808,7 @@ export class PageRenderer {
|
||||
async renderSinglePost(
|
||||
post: PostData,
|
||||
rewriteContext: HtmlRewriteContext,
|
||||
pageContext: { page_title: string; language: string },
|
||||
pageContext: { page_title: string; language: string; pico_stylesheet_href?: string; html_theme_attribute?: string },
|
||||
postEngine?: PostEngineContract,
|
||||
): Promise<string> {
|
||||
const renderablePost = postEngine
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
type MediaEngineContract,
|
||||
type PostMediaEngineContract,
|
||||
} from './PageRenderer';
|
||||
import { getPicoStylesheetHref, sanitizePicoTheme, sanitizePicoThemeMode } from '../shared/picoThemes';
|
||||
|
||||
interface ActiveProjectContext {
|
||||
projectId: string;
|
||||
@@ -172,11 +173,25 @@ export class PreviewServer {
|
||||
const language = metadata?.mainLanguage?.trim() || 'en';
|
||||
const pageTitle = resolvePageTitle(metadata, context.projectName, context.projectDescription);
|
||||
const maxPostsPerPage = clampMaxPostsPerPage(metadata?.maxPostsPerPage);
|
||||
const htmlRewriteContext = await this.buildHtmlRewriteContext();
|
||||
|
||||
const requestUrl = new URL(req.url || '/', 'http://127.0.0.1');
|
||||
const requestTheme = sanitizePicoTheme(requestUrl.searchParams.get('theme'));
|
||||
const previewThemeMode = sanitizePicoThemeMode(requestUrl.searchParams.get('mode'));
|
||||
const appliedTheme = requestTheme ?? sanitizePicoTheme((metadata as { picoTheme?: unknown } | null)?.picoTheme);
|
||||
const picoStylesheetHref = getPicoStylesheetHref(appliedTheme);
|
||||
const htmlRewriteContext = await this.buildHtmlRewriteContext();
|
||||
const pathname = decodeURIComponent(requestUrl.pathname.replace(/\/+$/, '') || '/');
|
||||
|
||||
if (pathname === '/__style-preview') {
|
||||
const stylePreviewHtml = await this.renderStylePreview(htmlRewriteContext, {
|
||||
pageTitle,
|
||||
language,
|
||||
picoStylesheetHref,
|
||||
htmlThemeAttribute: previewThemeMode && previewThemeMode !== 'auto' ? `data-theme="${previewThemeMode}"` : undefined,
|
||||
});
|
||||
this.respond(res, 200, stylePreviewHtml);
|
||||
return;
|
||||
}
|
||||
|
||||
const asset = await this.resolveAsset(pathname);
|
||||
if (asset) {
|
||||
this.respondAsset(res, asset.contentType, asset.body);
|
||||
@@ -198,11 +213,15 @@ export class PreviewServer {
|
||||
const result = await this.resolveRoute(pathname, maxPostsPerPage, htmlRewriteContext, {
|
||||
pageTitle,
|
||||
language,
|
||||
picoStylesheetHref,
|
||||
htmlThemeAttribute: undefined,
|
||||
});
|
||||
if (!result) {
|
||||
const notFoundHtml = await this.pageRenderer.renderNotFound({
|
||||
page_title: '404 Not Found',
|
||||
language,
|
||||
pico_stylesheet_href: picoStylesheetHref,
|
||||
html_theme_attribute: undefined,
|
||||
});
|
||||
this.respond(res, 404, notFoundHtml);
|
||||
return;
|
||||
@@ -219,7 +238,7 @@ export class PreviewServer {
|
||||
pathname: string,
|
||||
maxPostsPerPage: number,
|
||||
rewriteContext: HtmlRewriteContext,
|
||||
pageContext: { pageTitle: string; language: string },
|
||||
pageContext: { pageTitle: string; language: string; picoStylesheetHref: string; htmlThemeAttribute?: string },
|
||||
): Promise<string | null> {
|
||||
const routePagination = parseRoutePagination(pathname);
|
||||
if (!routePagination) {
|
||||
@@ -244,6 +263,8 @@ export class PreviewServer {
|
||||
return this.pageRenderer.renderSinglePost(post, rewriteContext, {
|
||||
page_title: pageContext.pageTitle,
|
||||
language: pageContext.language,
|
||||
pico_stylesheet_href: pageContext.picoStylesheetHref,
|
||||
html_theme_attribute: pageContext.htmlThemeAttribute,
|
||||
}, this.postEngine);
|
||||
}
|
||||
|
||||
@@ -255,6 +276,8 @@ export class PreviewServer {
|
||||
return this.pageRenderer.renderSinglePost(post, rewriteContext, {
|
||||
page_title: pageContext.pageTitle,
|
||||
language: pageContext.language,
|
||||
pico_stylesheet_href: pageContext.picoStylesheetHref,
|
||||
html_theme_attribute: pageContext.htmlThemeAttribute,
|
||||
}, this.postEngine);
|
||||
}
|
||||
|
||||
@@ -269,6 +292,8 @@ export class PreviewServer {
|
||||
return this.pageRenderer.renderSinglePost(post, rewriteContext, {
|
||||
page_title: pageContext.pageTitle,
|
||||
language: pageContext.language,
|
||||
pico_stylesheet_href: pageContext.picoStylesheetHref,
|
||||
html_theme_attribute: pageContext.htmlThemeAttribute,
|
||||
}, this.postEngine);
|
||||
}
|
||||
|
||||
@@ -280,6 +305,8 @@ export class PreviewServer {
|
||||
return this.pageRenderer.renderSinglePost(post, rewriteContext, {
|
||||
page_title: pageContext.pageTitle,
|
||||
language: pageContext.language,
|
||||
pico_stylesheet_href: pageContext.picoStylesheetHref,
|
||||
html_theme_attribute: pageContext.htmlThemeAttribute,
|
||||
}, this.postEngine);
|
||||
}
|
||||
|
||||
@@ -293,6 +320,8 @@ export class PreviewServer {
|
||||
pagination: { page, maxPostsPerPage, totalPosts: result.totalPosts },
|
||||
page_title: pageContext.pageTitle,
|
||||
language: pageContext.language,
|
||||
pico_stylesheet_href: pageContext.picoStylesheetHref,
|
||||
html_theme_attribute: pageContext.htmlThemeAttribute,
|
||||
}, this.postEngine);
|
||||
}
|
||||
|
||||
@@ -308,6 +337,8 @@ export class PreviewServer {
|
||||
pagination: { page, maxPostsPerPage, totalPosts: result.totalPosts },
|
||||
page_title: pageContext.pageTitle,
|
||||
language: pageContext.language,
|
||||
pico_stylesheet_href: pageContext.picoStylesheetHref,
|
||||
html_theme_attribute: pageContext.htmlThemeAttribute,
|
||||
}, this.postEngine);
|
||||
}
|
||||
|
||||
@@ -323,6 +354,8 @@ export class PreviewServer {
|
||||
pagination: { page, maxPostsPerPage, totalPosts: result.totalPosts },
|
||||
page_title: pageContext.pageTitle,
|
||||
language: pageContext.language,
|
||||
pico_stylesheet_href: pageContext.picoStylesheetHref,
|
||||
html_theme_attribute: pageContext.htmlThemeAttribute,
|
||||
}, this.postEngine);
|
||||
}
|
||||
|
||||
@@ -338,6 +371,8 @@ export class PreviewServer {
|
||||
return this.pageRenderer.renderSinglePost(post, rewriteContext, {
|
||||
page_title: pageContext.pageTitle,
|
||||
language: pageContext.language,
|
||||
pico_stylesheet_href: pageContext.picoStylesheetHref,
|
||||
html_theme_attribute: pageContext.htmlThemeAttribute,
|
||||
}, this.postEngine);
|
||||
}
|
||||
|
||||
@@ -355,6 +390,8 @@ export class PreviewServer {
|
||||
pagination: { page, maxPostsPerPage, totalPosts: result.totalPosts },
|
||||
page_title: pageContext.pageTitle,
|
||||
language: pageContext.language,
|
||||
pico_stylesheet_href: pageContext.picoStylesheetHref,
|
||||
html_theme_attribute: pageContext.htmlThemeAttribute,
|
||||
}, this.postEngine);
|
||||
}
|
||||
|
||||
@@ -372,6 +409,8 @@ export class PreviewServer {
|
||||
pagination: { page, maxPostsPerPage, totalPosts: result.totalPosts },
|
||||
page_title: pageContext.pageTitle,
|
||||
language: pageContext.language,
|
||||
pico_stylesheet_href: pageContext.picoStylesheetHref,
|
||||
html_theme_attribute: pageContext.htmlThemeAttribute,
|
||||
}, this.postEngine);
|
||||
}
|
||||
|
||||
@@ -387,6 +426,8 @@ export class PreviewServer {
|
||||
pagination: { page, maxPostsPerPage, totalPosts: result.totalPosts },
|
||||
page_title: pageContext.pageTitle,
|
||||
language: pageContext.language,
|
||||
pico_stylesheet_href: pageContext.picoStylesheetHref,
|
||||
html_theme_attribute: pageContext.htmlThemeAttribute,
|
||||
}, this.postEngine);
|
||||
}
|
||||
|
||||
@@ -399,12 +440,45 @@ export class PreviewServer {
|
||||
return this.pageRenderer.renderSinglePost(pagePost, rewriteContext, {
|
||||
page_title: pageContext.pageTitle,
|
||||
language: pageContext.language,
|
||||
pico_stylesheet_href: pageContext.picoStylesheetHref,
|
||||
html_theme_attribute: pageContext.htmlThemeAttribute,
|
||||
}, this.postEngine);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async renderStylePreview(
|
||||
rewriteContext: HtmlRewriteContext,
|
||||
pageContext: { pageTitle: string; language: string; picoStylesheetHref: string; htmlThemeAttribute?: string },
|
||||
): Promise<string> {
|
||||
const result = await this.loadPublishedSnapshotsPage({ status: 'published' }, {
|
||||
maxPostsPerPage: 10,
|
||||
page: 1,
|
||||
});
|
||||
|
||||
if (result.posts.length === 0) {
|
||||
return this.pageRenderer.renderNotFound({
|
||||
page_title: pageContext.pageTitle,
|
||||
language: pageContext.language,
|
||||
pico_stylesheet_href: pageContext.picoStylesheetHref,
|
||||
html_theme_attribute: pageContext.htmlThemeAttribute,
|
||||
});
|
||||
}
|
||||
|
||||
return this.pageRenderer.renderPostList(result.posts, rewriteContext, {
|
||||
archiveGrouping: true,
|
||||
routeKind: 'date',
|
||||
archiveContext: { kind: 'root' },
|
||||
basePathname: '/__style-preview',
|
||||
pagination: { page: 1, maxPostsPerPage: 10, totalPosts: result.totalPosts },
|
||||
page_title: pageContext.pageTitle,
|
||||
language: pageContext.language,
|
||||
pico_stylesheet_href: pageContext.picoStylesheetHref,
|
||||
html_theme_attribute: pageContext.htmlThemeAttribute,
|
||||
}, this.postEngine);
|
||||
}
|
||||
|
||||
private async findPublishedPostBySlug(slug: string, dateFilter?: { year: number; month: number }): Promise<PostData | null> {
|
||||
if (!slug) return null;
|
||||
|
||||
@@ -588,7 +662,7 @@ export class PreviewServer {
|
||||
const match = pathname.match(/^\/assets\/([^/]+)$/);
|
||||
if (!match) return null;
|
||||
|
||||
const assetName = match[1] as keyof typeof PREVIEW_ASSETS;
|
||||
const assetName = match[1];
|
||||
const assetDefinition = PREVIEW_ASSETS[assetName];
|
||||
if (!assetDefinition) return null;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!doctype html>
|
||||
<html lang="{{ language }}">
|
||||
{% render 'partials/head', page_title: page_title %}
|
||||
<html lang="{{ language }}"{% if html_theme_attribute %} {{ html_theme_attribute }}{% endif %}>
|
||||
{% render 'partials/head', page_title: page_title, pico_stylesheet_href: pico_stylesheet_href %}
|
||||
<body>
|
||||
<main>
|
||||
<section class="not-found" data-template="not-found">
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{{ page_title }}</title>
|
||||
<link rel="stylesheet" href="/assets/pico.min.css" />
|
||||
{% assign resolved_pico_stylesheet_href = pico_stylesheet_href | default: '/assets/pico.min.css' %}
|
||||
<link rel="stylesheet" href="{{ resolved_pico_stylesheet_href }}" />
|
||||
<link rel="stylesheet" href="/assets/lightbox.min.css" />
|
||||
{% render 'partials/styles' %}
|
||||
<script defer src="/assets/lightbox.min.js"></script>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<style>
|
||||
:root { color-scheme: light dark; }
|
||||
body { max-width: 960px; margin: 0 auto; padding: 2rem 1rem 4rem; }
|
||||
body { max-width: 960px; margin: 0 auto; padding: 2rem 1rem 4rem; background: var(--pico-background-color, var(--background-color)); color: var(--pico-color, var(--color)); }
|
||||
main { display: grid; gap: 1rem; }
|
||||
.post { border: 1px solid var(--muted-border-color); padding: 1rem; background: var(--card-background-color); }
|
||||
.post { border: 1px solid var(--pico-muted-border-color, var(--muted-border-color)); padding: 1rem; background: var(--pico-card-background-color, var(--card-background-color)); }
|
||||
.post iframe { width: 100%; min-height: 20rem; }
|
||||
.macro-gallery, .macro-photo-archive { border: 1px dashed var(--muted-border-color); padding: .75rem; margin: 1rem 0; }
|
||||
.macro-gallery, .macro-photo-archive { border: 1px dashed var(--pico-muted-border-color, var(--muted-border-color)); padding: .75rem; margin: 1rem 0; }
|
||||
.gallery-container { display: grid; gap: .5rem; }
|
||||
.macro-gallery.gallery-cols-1 .gallery-container { grid-template-columns: 1fr; }
|
||||
.macro-gallery.gallery-cols-2 .gallery-container { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
@@ -14,23 +14,23 @@
|
||||
.macro-gallery.gallery-cols-6 .gallery-container { grid-template-columns: repeat(6, minmax(0, 1fr)); }
|
||||
.gallery-item, .photo-archive-item { display: block; overflow: hidden; border-radius: .25rem; }
|
||||
.gallery-item img, .photo-archive-item img { display: block; width: 100%; height: auto; aspect-ratio: 1 / 1; object-fit: cover; }
|
||||
.gallery-caption { margin-top: .5rem; text-align: center; color: var(--muted-color); font-size: .92rem; }
|
||||
.gallery-empty, .photo-archive-empty { color: var(--muted-color); font-style: italic; }
|
||||
.gallery-caption { margin-top: .5rem; text-align: center; color: var(--pico-muted-color, var(--muted-color)); font-size: .92rem; }
|
||||
.gallery-empty, .photo-archive-empty { color: var(--pico-muted-color, var(--muted-color)); font-style: italic; }
|
||||
.photo-archive-container { display: grid; gap: 1rem; }
|
||||
.photo-archive-month { display: grid; grid-template-columns: 3.25rem 1fr; gap: .75rem; align-items: start; }
|
||||
.photo-archive-month-label { display: flex; justify-content: center; align-items: center; }
|
||||
.photo-archive-month-label span { writing-mode: vertical-rl; transform: rotate(180deg); letter-spacing: .08em; text-transform: uppercase; color: var(--muted-color); }
|
||||
.photo-archive-month-label span { writing-mode: vertical-rl; transform: rotate(180deg); letter-spacing: .08em; text-transform: uppercase; color: var(--pico-muted-color, var(--muted-color)); }
|
||||
.photo-archive-gallery { display: grid; gap: .5rem; grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
||||
.photo-archive-single-month .photo-archive-gallery { grid-template-columns: repeat(5, minmax(0, 1fr)); }
|
||||
.archive-day-group { display: grid; grid-template-columns: 5.25rem 1fr; gap: 1.25rem; align-items: stretch; }
|
||||
.archive-day-marker { display: flex; justify-content: center; align-items: center; color: var(--muted-color); }
|
||||
.archive-day-marker { display: flex; justify-content: center; align-items: center; color: var(--pico-muted-color, var(--muted-color)); }
|
||||
.archive-day-marker span { writing-mode: vertical-rl; transform: rotate(180deg); letter-spacing: .16em; font-size: 1.05rem; font-weight: 600; text-transform: uppercase; }
|
||||
.archive-day-posts { display: grid; gap: 1rem; }
|
||||
.archive-day-separator { position: relative; height: 2px; width: 100%; color: var(--color); border-top: 1px solid currentColor; opacity: .18; margin: .45rem 0 .65rem; }
|
||||
.archive-day-separator { position: relative; height: 2px; width: 100%; color: var(--pico-color, var(--color)); border-top: 1px solid currentColor; opacity: .18; margin: .45rem 0 .65rem; }
|
||||
.archive-day-separator::before { content: ''; position: absolute; inset: 0; background: linear-gradient(to right, transparent 0%, transparent 18%, currentColor 58%, transparent 92%, transparent 100%); opacity: .85; }
|
||||
.single-post { margin: 0; padding: 0; background: transparent; border: 0; box-shadow: none; }
|
||||
.preview-pagination { display: flex; justify-content: space-between; align-items: center; gap: .75rem; margin-top: .25rem; }
|
||||
.preview-pagination-link { color: var(--muted-color); text-decoration: none; font-size: .92rem; opacity: .72; transition: opacity .15s ease-in-out; }
|
||||
.preview-pagination-link { color: var(--pico-muted-color, var(--muted-color)); text-decoration: none; font-size: .92rem; opacity: .72; transition: opacity .15s ease-in-out; }
|
||||
.preview-pagination-link:hover,
|
||||
.preview-pagination-link:focus-visible { opacity: 1; text-decoration: underline; }
|
||||
.preview-pagination .spacer { flex: 1; }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!doctype html>
|
||||
<html lang="{{ language }}">
|
||||
{% render 'partials/head', page_title: page_title %}
|
||||
<html lang="{{ language }}"{% if html_theme_attribute %} {{ html_theme_attribute }}{% endif %}>
|
||||
{% render 'partials/head', page_title: page_title, pico_stylesheet_href: pico_stylesheet_href %}
|
||||
<body>
|
||||
<main>
|
||||
{% if archive_context %}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!doctype html>
|
||||
<html lang="{{ language }}">
|
||||
{% render 'partials/head', page_title: page_title %}
|
||||
<html lang="{{ language }}"{% if html_theme_attribute %} {{ html_theme_attribute }}{% endif %}>
|
||||
{% render 'partials/head', page_title: page_title, pico_stylesheet_href: pico_stylesheet_href %}
|
||||
<body>
|
||||
<main>
|
||||
<article class="single-post" data-template="single-post">
|
||||
|
||||
Reference in New Issue
Block a user