mirror of
				https://github.com/jaandrle/deka-dom-el
				synced 2025-11-04 15:19:15 +01:00 
			
		
		
		
	🔤 ⚡ Docs UI/UX
This commit is contained in:
		@@ -1,28 +1,174 @@
 | 
			
		||||
import { registerClientFile, styles } from "../ssr.js";
 | 
			
		||||
const host= "."+code.name;
 | 
			
		||||
styles.css`
 | 
			
		||||
${host}{
 | 
			
		||||
	--shiki-color-text: #e9eded;
 | 
			
		||||
	--shiki-color-background: #212121;
 | 
			
		||||
/* Code block styling */
 | 
			
		||||
${host} {
 | 
			
		||||
	/* Theme for dark mode - matches Flems/CodeMirror dark theme */
 | 
			
		||||
	--shiki-color-text: #f8f8f2;
 | 
			
		||||
	--shiki-color-background: var(--code-bg);
 | 
			
		||||
	--shiki-token-constant: #82b1ff;
 | 
			
		||||
	--shiki-token-string: #c3e88d;
 | 
			
		||||
	--shiki-token-comment: #546e7a;
 | 
			
		||||
	--shiki-token-keyword: #c792ea;
 | 
			
		||||
	--shiki-token-parameter: #AA0000;
 | 
			
		||||
	--shiki-token-parameter: #fd971f;
 | 
			
		||||
	--shiki-token-function: #80cbae;
 | 
			
		||||
	--shiki-token-string-expression: #c3e88d;
 | 
			
		||||
	--shiki-token-punctuation: var(--code);
 | 
			
		||||
	--shiki-token-link: #EE0000;
 | 
			
		||||
	--shiki-token-punctuation: #89ddff;
 | 
			
		||||
	--shiki-token-link: #82aaff;
 | 
			
		||||
	--shiki-token-variable: #f8f8f2;
 | 
			
		||||
	--shiki-token-number: #f78c6c;
 | 
			
		||||
	--shiki-token-boolean: #82b1ff;
 | 
			
		||||
	--shiki-token-tag: #f07178;
 | 
			
		||||
	--shiki-token-attribute: #ffcb6b;
 | 
			
		||||
	--shiki-token-property: #82b1ff;
 | 
			
		||||
	--shiki-token-operator: #89ddff;
 | 
			
		||||
	--shiki-token-regex: #c3e88d;
 | 
			
		||||
	--shiki-token-class: #ffcb6b;
 | 
			
		||||
 | 
			
		||||
	/* Basic styling */
 | 
			
		||||
	white-space: pre;
 | 
			
		||||
	tab-size: 2; /* TODO: allow custom tab size?! */
 | 
			
		||||
	overflow: scroll;
 | 
			
		||||
	tab-size: 2;
 | 
			
		||||
	overflow: auto;
 | 
			
		||||
	border-radius: var(--border-radius);
 | 
			
		||||
	font-family: var(--font-mono);
 | 
			
		||||
	font-size: 0.9rem;
 | 
			
		||||
	line-height: 1.5;
 | 
			
		||||
	position: relative;
 | 
			
		||||
}
 | 
			
		||||
${host}[data-js=todo]{
 | 
			
		||||
 | 
			
		||||
/* Light mode overrides to match GitHub-like theme */
 | 
			
		||||
@media (prefers-color-scheme: light) {
 | 
			
		||||
	${host} {
 | 
			
		||||
		--shiki-color-text: #24292e;
 | 
			
		||||
		--shiki-color-background: var(--code-bg);
 | 
			
		||||
		--shiki-token-constant: #005cc5;
 | 
			
		||||
		--shiki-token-string: #22863a;
 | 
			
		||||
		--shiki-token-comment: #6a737d;
 | 
			
		||||
		--shiki-token-keyword: #d73a49;
 | 
			
		||||
		--shiki-token-parameter: #e36209;
 | 
			
		||||
		--shiki-token-function: #6f42c1;
 | 
			
		||||
		--shiki-token-string-expression: #22863a;
 | 
			
		||||
		--shiki-token-punctuation: #24292e;
 | 
			
		||||
		--shiki-token-link: #0366d6;
 | 
			
		||||
		--shiki-token-variable: #24292e;
 | 
			
		||||
		--shiki-token-number: #005cc5;
 | 
			
		||||
		--shiki-token-boolean: #005cc5;
 | 
			
		||||
		--shiki-token-tag: #22863a;
 | 
			
		||||
		--shiki-token-attribute: #005cc5;
 | 
			
		||||
		--shiki-token-property: #005cc5;
 | 
			
		||||
		--shiki-token-operator: #d73a49;
 | 
			
		||||
		--shiki-token-regex: #032f62;
 | 
			
		||||
		--shiki-token-class: #6f42c1;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Support for theme toggles */
 | 
			
		||||
html[data-theme="light"] ${host} {
 | 
			
		||||
	--shiki-color-text: #24292e;
 | 
			
		||||
	--shiki-color-background: var(--code-bg);
 | 
			
		||||
	--shiki-token-constant: #005cc5;
 | 
			
		||||
	--shiki-token-string: #22863a;
 | 
			
		||||
	--shiki-token-comment: #6a737d;
 | 
			
		||||
	--shiki-token-keyword: #d73a49;
 | 
			
		||||
	--shiki-token-parameter: #e36209;
 | 
			
		||||
	--shiki-token-function: #6f42c1;
 | 
			
		||||
	--shiki-token-string-expression: #22863a;
 | 
			
		||||
	--shiki-token-punctuation: #24292e;
 | 
			
		||||
	--shiki-token-link: #0366d6;
 | 
			
		||||
	--shiki-token-variable: #24292e;
 | 
			
		||||
	--shiki-token-number: #005cc5;
 | 
			
		||||
	--shiki-token-boolean: #005cc5;
 | 
			
		||||
	--shiki-token-tag: #22863a;
 | 
			
		||||
	--shiki-token-attribute: #005cc5;
 | 
			
		||||
	--shiki-token-property: #005cc5;
 | 
			
		||||
	--shiki-token-operator: #d73a49;
 | 
			
		||||
	--shiki-token-regex: #032f62;
 | 
			
		||||
	--shiki-token-class: #6f42c1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
html[data-theme="dark"] ${host} {
 | 
			
		||||
	--shiki-color-text: #f8f8f2;
 | 
			
		||||
	--shiki-color-background: var(--code-bg);
 | 
			
		||||
	--shiki-token-constant: #82b1ff;
 | 
			
		||||
	--shiki-token-string: #c3e88d;
 | 
			
		||||
	--shiki-token-comment: #546e7a;
 | 
			
		||||
	--shiki-token-keyword: #c792ea;
 | 
			
		||||
	--shiki-token-parameter: #fd971f;
 | 
			
		||||
	--shiki-token-function: #80cbae;
 | 
			
		||||
	--shiki-token-string-expression: #c3e88d;
 | 
			
		||||
	--shiki-token-punctuation: #89ddff;
 | 
			
		||||
	--shiki-token-link: #82aaff;
 | 
			
		||||
	--shiki-token-variable: #f8f8f2;
 | 
			
		||||
	--shiki-token-number: #f78c6c;
 | 
			
		||||
	--shiki-token-boolean: #82b1ff;
 | 
			
		||||
	--shiki-token-tag: #f07178;
 | 
			
		||||
	--shiki-token-attribute: #ffcb6b;
 | 
			
		||||
	--shiki-token-property: #82b1ff;
 | 
			
		||||
	--shiki-token-operator: #89ddff;
 | 
			
		||||
	--shiki-token-regex: #c3e88d;
 | 
			
		||||
	--shiki-token-class: #ffcb6b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Code block with syntax highlighting waiting for JS */
 | 
			
		||||
${host}[data-js=todo] {
 | 
			
		||||
	border: 1px solid var(--border);
 | 
			
		||||
	border-radius: var(--standard-border-radius);
 | 
			
		||||
	margin-bottom: 1rem;
 | 
			
		||||
	margin-top: 18.4px; /* to fix shift when → dataJS=done */
 | 
			
		||||
	padding: 1rem 1.4rem;
 | 
			
		||||
	border-radius: var(--border-radius);
 | 
			
		||||
	margin-bottom: 1.5rem;
 | 
			
		||||
	margin-top: 1rem;
 | 
			
		||||
	padding: 1rem;
 | 
			
		||||
	background-color: var(--code-bg);
 | 
			
		||||
	position: relative;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add a subtle loading indicator */
 | 
			
		||||
${host}[data-js=todo]::before {
 | 
			
		||||
	content: "Loading syntax highlighting...";
 | 
			
		||||
	position: absolute;
 | 
			
		||||
	top: 0.5rem;
 | 
			
		||||
	right: 0.5rem;
 | 
			
		||||
	font-size: 0.75rem;
 | 
			
		||||
	color: var(--text-light);
 | 
			
		||||
	background-color: var(--bg);
 | 
			
		||||
	padding: 0.25rem 0.5rem;
 | 
			
		||||
	border-radius: var(--border-radius);
 | 
			
		||||
	opacity: 0.7;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* All code blocks should have consistent font and sizing */
 | 
			
		||||
${host} code {
 | 
			
		||||
	font-family: var(--font-mono);
 | 
			
		||||
	font-size: 0.9rem;
 | 
			
		||||
	line-height: 1.5;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Ensure line numbers (if added) are styled appropriately */
 | 
			
		||||
${host} .line-number {
 | 
			
		||||
	user-select: none;
 | 
			
		||||
	opacity: 0.5;
 | 
			
		||||
	margin-right: 1rem;
 | 
			
		||||
	min-width: 1.5rem;
 | 
			
		||||
	display: inline-block;
 | 
			
		||||
	text-align: right;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* If there's a copy button, style it */
 | 
			
		||||
${host} .copy-button {
 | 
			
		||||
	position: absolute;
 | 
			
		||||
	top: 0.5rem;
 | 
			
		||||
	right: 0.5rem;
 | 
			
		||||
	background-color: var(--bg);
 | 
			
		||||
	color: var(--text);
 | 
			
		||||
	border: 1px solid var(--border);
 | 
			
		||||
	border-radius: var(--border-radius);
 | 
			
		||||
	padding: 0.25rem 0.5rem;
 | 
			
		||||
	font-size: 0.75rem;
 | 
			
		||||
	cursor: pointer;
 | 
			
		||||
	opacity: 0;
 | 
			
		||||
	transition: opacity 0.2s ease;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
${host}:hover .copy-button {
 | 
			
		||||
	opacity: 1;
 | 
			
		||||
}
 | 
			
		||||
`;
 | 
			
		||||
import { el } from "deka-dom-el";
 | 
			
		||||
@@ -52,12 +198,46 @@ let is_registered= {};
 | 
			
		||||
function registerClientPart(page_id){
 | 
			
		||||
	if(is_registered[page_id]) return;
 | 
			
		||||
 | 
			
		||||
	// Add Shiki with a more reliable loading method
 | 
			
		||||
	document.head.append(
 | 
			
		||||
		el("script", { src: "https://cdn.jsdelivr.net/npm/shiki@0.9", defer: true }),
 | 
			
		||||
		// Use a newer version of Shiki with better performance
 | 
			
		||||
		el("script", { src: "https://cdn.jsdelivr.net/npm/shiki@0.14.3/dist/index.unpkg.iife.js", defer: true }),
 | 
			
		||||
		// Make sure we can match Flems styling in dark/light mode
 | 
			
		||||
		el("style", `
 | 
			
		||||
			/* Ensure CodeMirror and Shiki use the same font */
 | 
			
		||||
			.CodeMirror *, .shiki * {
 | 
			
		||||
				font-family: var(--font-mono) !important;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* Style Shiki's output to match our theme */
 | 
			
		||||
			.shiki {
 | 
			
		||||
				background-color: var(--shiki-color-background) !important;
 | 
			
		||||
				color: var(--shiki-color-text) !important;
 | 
			
		||||
				padding: 1rem;
 | 
			
		||||
				border-radius: var(--border-radius);
 | 
			
		||||
				tab-size: 2;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* Ensure Shiki code tokens use our CSS variables */
 | 
			
		||||
			.shiki .keyword { color: var(--shiki-token-keyword) !important; }
 | 
			
		||||
			.shiki .constant { color: var(--shiki-token-constant) !important; }
 | 
			
		||||
			.shiki .string { color: var(--shiki-token-string) !important; }
 | 
			
		||||
			.shiki .comment { color: var(--shiki-token-comment) !important; }
 | 
			
		||||
			.shiki .function { color: var(--shiki-token-function) !important; }
 | 
			
		||||
			.shiki .operator, .shiki .punctuation { color: var(--shiki-token-punctuation) !important; }
 | 
			
		||||
			.shiki .parameter { color: var(--shiki-token-parameter) !important; }
 | 
			
		||||
			.shiki .variable { color: var(--shiki-token-variable) !important; }
 | 
			
		||||
			.shiki .property { color: var(--shiki-token-property) !important; }
 | 
			
		||||
		`),
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	// Register our highlighting script to run after Shiki loads
 | 
			
		||||
	const scriptElement = el("script", { type: "module" });
 | 
			
		||||
 | 
			
		||||
	registerClientFile(
 | 
			
		||||
		new URL("./code.js.js", import.meta.url),
 | 
			
		||||
		el("script", { type: "module" })
 | 
			
		||||
		scriptElement
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	is_registered[page_id]= true;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,61 @@
 | 
			
		||||
const highlighter= await globalThis.shiki.getHighlighter({
 | 
			
		||||
	theme: "css-variables",
 | 
			
		||||
	langs: ["js", "ts", "css", "html", "shell"],
 | 
			
		||||
});
 | 
			
		||||
const codeBlocks= document.querySelectorAll('code[class*="language-"]');
 | 
			
		||||
try {
 | 
			
		||||
	// Initialize Shiki with our custom theme
 | 
			
		||||
	const highlighter = await globalThis.shiki.getHighlighter({
 | 
			
		||||
		theme: "css-variables",
 | 
			
		||||
		langs: ["javascript", "typescript", "css", "html", "shell"],
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
codeBlocks.forEach((block)=> {
 | 
			
		||||
	const lang= block.className.replace("language-", "");
 | 
			
		||||
	block.parentElement.dataset.js= "done";
 | 
			
		||||
	const html= highlighter.codeToHtml(block.textContent, { lang });
 | 
			
		||||
	block.innerHTML= html;
 | 
			
		||||
});
 | 
			
		||||
	// Find all code blocks that need highlighting
 | 
			
		||||
	const codeBlocks = document.querySelectorAll('div[data-js="todo"] code[class*="language-"]');
 | 
			
		||||
 | 
			
		||||
	// Process each code block
 | 
			
		||||
	codeBlocks.forEach((block) => {
 | 
			
		||||
		try {
 | 
			
		||||
			// Get the language from the class
 | 
			
		||||
			const langClass = block.className.match(/language-(\w+)/);
 | 
			
		||||
 | 
			
		||||
			// Map the language to Shiki format
 | 
			
		||||
			let lang = langClass ? langClass[1] : 'javascript';
 | 
			
		||||
			if (lang === 'js') lang = 'javascript';
 | 
			
		||||
			if (lang === 'ts') lang = 'typescript';
 | 
			
		||||
 | 
			
		||||
			// Mark the container as processed
 | 
			
		||||
			block.parentElement.dataset.js = "done";
 | 
			
		||||
 | 
			
		||||
			// Highlight the code
 | 
			
		||||
			const code = block.textContent;
 | 
			
		||||
			const html = highlighter.codeToHtml(code, { lang });
 | 
			
		||||
 | 
			
		||||
			// Insert the highlighted HTML
 | 
			
		||||
			block.innerHTML = html;
 | 
			
		||||
 | 
			
		||||
			// Add copy button functionality
 | 
			
		||||
			const copyBtn = document.createElement('button');
 | 
			
		||||
			copyBtn.className = 'copy-button';
 | 
			
		||||
			copyBtn.textContent = 'Copy';
 | 
			
		||||
			copyBtn.setAttribute('aria-label', 'Copy code to clipboard');
 | 
			
		||||
			copyBtn.addEventListener('click', () => {
 | 
			
		||||
				navigator.clipboard.writeText(code).then(() => {
 | 
			
		||||
					copyBtn.textContent = 'Copied!';
 | 
			
		||||
					setTimeout(() => {
 | 
			
		||||
						copyBtn.textContent = 'Copy';
 | 
			
		||||
					}, 2000);
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			// Add the copy button to the code block container
 | 
			
		||||
			block.parentElement.appendChild(copyBtn);
 | 
			
		||||
		} catch (err) {
 | 
			
		||||
			console.error('Error highlighting code block:', err);
 | 
			
		||||
			// Make sure we don't leave the block in a pending state
 | 
			
		||||
			block.parentElement.dataset.js = "error";
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
} catch (err) {
 | 
			
		||||
	console.error('Failed to initialize Shiki:', err);
 | 
			
		||||
 | 
			
		||||
	// Fallback: at least mark blocks as processed so they don't show loading indicator
 | 
			
		||||
	document.querySelectorAll('div[data-js="todo"]').forEach(block => {
 | 
			
		||||
		block.dataset.js = "error";
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,103 @@
 | 
			
		||||
import { styles } from "../ssr.js";
 | 
			
		||||
const host= "."+example.name;
 | 
			
		||||
styles.css`
 | 
			
		||||
${host}{
 | 
			
		||||
${host} {
 | 
			
		||||
	grid-column: full-main;
 | 
			
		||||
	width: 100%;
 | 
			
		||||
	max-width: calc(9/5 * var(--body-max-width));
 | 
			
		||||
	height: calc(3/5 * var(--body-max-width));
 | 
			
		||||
	margin-inline: auto;
 | 
			
		||||
	margin: 2rem auto;
 | 
			
		||||
	border-radius: var(--border-radius);
 | 
			
		||||
	box-shadow: var(--shadow);
 | 
			
		||||
	border: 1px solid var(--border);
 | 
			
		||||
}
 | 
			
		||||
${host} .runtime {
 | 
			
		||||
	background-color: whitesmoke;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* CodeMirror styling to match our theme */
 | 
			
		||||
.CodeMirror {
 | 
			
		||||
	height: 100% !important;
 | 
			
		||||
	font-family: var(--font-mono) !important;
 | 
			
		||||
	font-size: 0.95rem !important;
 | 
			
		||||
	line-height: 1.5 !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Dark mode styles for CodeMirror */
 | 
			
		||||
.CodeMirror, .CodeMirror-gutters {
 | 
			
		||||
	background: #212121 !important;
 | 
			
		||||
	border: 1px solid white;
 | 
			
		||||
	background: var(--code-bg) !important;
 | 
			
		||||
	border: 1px solid var(--border) !important;
 | 
			
		||||
	color: var(--text) !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Light mode adjustments for CodeMirror - using CSS variables */
 | 
			
		||||
@media (prefers-color-scheme: light) {
 | 
			
		||||
	/* Core syntax elements */
 | 
			
		||||
	.cm-s-material .cm-keyword { color: var(--shiki-token-keyword, #d73a49) !important; }
 | 
			
		||||
	.cm-s-material .cm-atom { color: var(--shiki-token-constant, #005cc5) !important; }
 | 
			
		||||
	.cm-s-material .cm-number { color: var(--shiki-token-number, #005cc5) !important; }
 | 
			
		||||
	.cm-s-material .cm-def { color: var(--shiki-token-function, #6f42c1) !important; }
 | 
			
		||||
	.cm-s-material .cm-variable { color: var(--shiki-token-variable, #24292e) !important; }
 | 
			
		||||
	.cm-s-material .cm-variable-2 { color: var(--shiki-token-variable, #24292e) !important; }
 | 
			
		||||
	.cm-s-material .cm-variable-3 { color: var(--shiki-token-variable, #24292e) !important; }
 | 
			
		||||
	.cm-s-material .cm-property { color: var(--shiki-token-property, #005cc5) !important; }
 | 
			
		||||
	.cm-s-material .cm-operator { color: var(--shiki-token-operator, #d73a49) !important; }
 | 
			
		||||
	.cm-s-material .cm-comment { color: var(--shiki-token-comment, #6a737d) !important; }
 | 
			
		||||
	.cm-s-material .cm-string { color: var(--shiki-token-string, #22863a) !important; }
 | 
			
		||||
	.cm-s-material .cm-string-2 { color: var(--shiki-token-string, #22863a) !important; }
 | 
			
		||||
	.cm-s-material .cm-tag { color: var(--shiki-token-tag, #22863a) !important; }
 | 
			
		||||
	.cm-s-material .cm-attribute { color: var(--shiki-token-attribute, #005cc5) !important; }
 | 
			
		||||
	.cm-s-material .cm-bracket { color: var(--shiki-token-punctuation, #24292e) !important; }
 | 
			
		||||
	.cm-s-material .cm-punctuation { color: var(--shiki-token-punctuation, #24292e) !important; }
 | 
			
		||||
	.cm-s-material .cm-link { color: var(--shiki-token-link, #0366d6) !important; }
 | 
			
		||||
	.cm-s-material .cm-error { color: #f44336 !important; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle theme toggle */
 | 
			
		||||
html[data-theme="light"] .CodeMirror {
 | 
			
		||||
	background: #f5f7fa !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
html[data-theme="light"] .CodeMirror-gutters {
 | 
			
		||||
	background: #f5f7fa !important;
 | 
			
		||||
	border-right: 1px solid #e5e7eb !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Also apply the same styles to CodeMirror with data-theme */
 | 
			
		||||
html[data-theme="light"] .cm-s-material .cm-keyword { color: var(--shiki-token-keyword, #d73a49) !important; }
 | 
			
		||||
html[data-theme="light"] .cm-s-material .cm-atom { color: var(--shiki-token-constant, #005cc5) !important; }
 | 
			
		||||
html[data-theme="light"] .cm-s-material .cm-number { color: var(--shiki-token-number, #005cc5) !important; }
 | 
			
		||||
html[data-theme="light"] .cm-s-material .cm-def { color: var(--shiki-token-function, #6f42c1) !important; }
 | 
			
		||||
html[data-theme="light"] .cm-s-material .cm-variable { color: var(--shiki-token-variable, #24292e) !important; }
 | 
			
		||||
html[data-theme="light"] .cm-s-material .cm-variable-2 { color: var(--shiki-token-variable, #24292e) !important; }
 | 
			
		||||
html[data-theme="light"] .cm-s-material .cm-variable-3 { color: var(--shiki-token-variable, #24292e) !important; }
 | 
			
		||||
html[data-theme="light"] .cm-s-material .cm-property { color: var(--shiki-token-property, #005cc5) !important; }
 | 
			
		||||
html[data-theme="light"] .cm-s-material .cm-operator { color: var(--shiki-token-operator, #d73a49) !important; }
 | 
			
		||||
html[data-theme="light"] .cm-s-material .cm-comment { color: var(--shiki-token-comment, #6a737d) !important; }
 | 
			
		||||
html[data-theme="light"] .cm-s-material .cm-string { color: var(--shiki-token-string, #22863a) !important; }
 | 
			
		||||
html[data-theme="light"] .cm-s-material .cm-string-2 { color: var(--shiki-token-string, #22863a) !important; }
 | 
			
		||||
html[data-theme="light"] .cm-s-material .cm-tag { color: var(--shiki-token-tag, #22863a) !important; }
 | 
			
		||||
html[data-theme="light"] .cm-s-material .cm-attribute { color: var(--shiki-token-attribute, #005cc5) !important; }
 | 
			
		||||
html[data-theme="light"] .cm-s-material .cm-bracket { color: var(--shiki-token-punctuation, #24292e) !important; }
 | 
			
		||||
html[data-theme="light"] .cm-s-material .cm-punctuation { color: var(--shiki-token-punctuation, #24292e) !important; }
 | 
			
		||||
html[data-theme="light"] .cm-s-material .cm-link { color: var(--shiki-token-link, #0366d6) !important; }
 | 
			
		||||
html[data-theme="light"] .cm-s-material .cm-error { color: #f44336 !important; }
 | 
			
		||||
 | 
			
		||||
/* Mobile adjustments */
 | 
			
		||||
@media (max-width: 767px) {
 | 
			
		||||
	${host} {
 | 
			
		||||
		height: 50vh;
 | 
			
		||||
		max-width: 100%;
 | 
			
		||||
	}
 | 
			
		||||
	${host} main {
 | 
			
		||||
		flex-grow: 1;
 | 
			
		||||
		display: flex;
 | 
			
		||||
		flex-direction: column;
 | 
			
		||||
	}
 | 
			
		||||
	${host} main > * {
 | 
			
		||||
		width: 100%;
 | 
			
		||||
		max-width: 100% !important;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +1,67 @@
 | 
			
		||||
import { pages, styles } from "../ssr.js";
 | 
			
		||||
const host= "."+prevNext.name;
 | 
			
		||||
styles.css`
 | 
			
		||||
${host}{
 | 
			
		||||
	display: grid;
 | 
			
		||||
	grid-template-columns: 1fr 2fr 1fr;
 | 
			
		||||
	margin-top: 1rem;
 | 
			
		||||
/* Previous/Next navigation */
 | 
			
		||||
${host} {
 | 
			
		||||
	display: flex;
 | 
			
		||||
	justify-content: space-between;
 | 
			
		||||
	margin-top: 3rem;
 | 
			
		||||
	padding-top: 1.5rem;
 | 
			
		||||
	border-top: 1px solid var(--border);
 | 
			
		||||
	gap: 1rem;
 | 
			
		||||
}
 | 
			
		||||
${host} [rel=prev]{
 | 
			
		||||
	grid-column: 1;
 | 
			
		||||
 | 
			
		||||
${host} a {
 | 
			
		||||
	display: flex;
 | 
			
		||||
	align-items: center;
 | 
			
		||||
	padding: 0.75rem 1.25rem;
 | 
			
		||||
	border-radius: var(--border-radius);
 | 
			
		||||
	background-color: var(--primary-dark); /* Darker background for better contrast */
 | 
			
		||||
	color: white;
 | 
			
		||||
	font-weight: 600; /* Bolder text for better readability */
 | 
			
		||||
	text-decoration: none;
 | 
			
		||||
	transition: background-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
 | 
			
		||||
	max-width: 45%;
 | 
			
		||||
	box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15); /* Subtle shadow for better visibility */
 | 
			
		||||
}
 | 
			
		||||
${host} [rel=next]{
 | 
			
		||||
	grid-column: 3;
 | 
			
		||||
	text-align: right;
 | 
			
		||||
 | 
			
		||||
${host} a:hover {
 | 
			
		||||
	background-color: var(--primary);
 | 
			
		||||
	transform: translateY(-2px);
 | 
			
		||||
	text-decoration: none;
 | 
			
		||||
	box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* Enhanced shadow on hover */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
${host} [rel=prev] {
 | 
			
		||||
	margin-right: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
${host} [rel=next] {
 | 
			
		||||
	margin-left: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
${host} [rel=prev]::before {
 | 
			
		||||
	content: "←";
 | 
			
		||||
	margin-right: 0.75rem;
 | 
			
		||||
	font-size: 1.2em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
${host} [rel=next]::after {
 | 
			
		||||
	content: "→";
 | 
			
		||||
	margin-left: 0.75rem;
 | 
			
		||||
	font-size: 1.2em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* If there's no previous/next, ensure the spacing still works */
 | 
			
		||||
${host} a:only-child {
 | 
			
		||||
	margin-left: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (max-width: 640px) {
 | 
			
		||||
	${host} a {
 | 
			
		||||
		padding: 0.5rem 0.75rem;
 | 
			
		||||
		font-size: 0.9rem;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
`;
 | 
			
		||||
import { el } from "../../index.js";
 | 
			
		||||
@@ -22,21 +71,28 @@ import { el } from "../../index.js";
 | 
			
		||||
 * @param {string} [attrs.id]
 | 
			
		||||
 * */
 | 
			
		||||
export function h3({ textContent, id }){
 | 
			
		||||
	if(!id) id= "h-"+textContent.toLowerCase().replaceAll(/\s/g, "-").replaceAll(/[^a-z-]/g, "");
 | 
			
		||||
	return el("h3", { id }).append(
 | 
			
		||||
		el("a", { textContent: "#", href: "#"+id, tabIndex: -1 }),
 | 
			
		||||
		" ", textContent
 | 
			
		||||
	);
 | 
			
		||||
		if(!id) id= "h-"+textContent.toLowerCase().replaceAll(/\s/g, "-").replaceAll(/[^a-z-]/g, "");
 | 
			
		||||
		return el("h3", { id, className: "section-heading" }).append(
 | 
			
		||||
				el("a", {
 | 
			
		||||
						className: "heading-anchor",
 | 
			
		||||
						href: "#"+id,
 | 
			
		||||
						textContent: "#",
 | 
			
		||||
						title: `Link to this section: ${textContent}`,
 | 
			
		||||
						"aria-label": `Link to section ${textContent}`
 | 
			
		||||
				}),
 | 
			
		||||
				" ",
 | 
			
		||||
				textContent,
 | 
			
		||||
		);
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 * @param {import("../types.d.ts").Info} page
 | 
			
		||||
 * */
 | 
			
		||||
export function prevNext(page){
 | 
			
		||||
	const page_index= pages.indexOf(page);
 | 
			
		||||
	return el("div", { className: prevNext.name }).append(
 | 
			
		||||
		el(pageLink, { rel: "prev", page: pages[page_index-1] }),
 | 
			
		||||
		el(pageLink, { rel: "next", page: pages[page_index+1] })
 | 
			
		||||
	);
 | 
			
		||||
		const page_index= pages.indexOf(page);
 | 
			
		||||
		return el("div", { className: prevNext.name }).append(
 | 
			
		||||
				el(pageLink, { rel: "prev", page: pages[page_index-1] }),
 | 
			
		||||
				el(pageLink, { rel: "next", page: pages[page_index+1] })
 | 
			
		||||
		);
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 * @param {Object} attrs
 | 
			
		||||
@@ -44,11 +100,22 @@ export function prevNext(page){
 | 
			
		||||
 * @param {import("../types.d.ts").Info} [attrs.page]
 | 
			
		||||
 * */
 | 
			
		||||
function pageLink({ rel, page }){
 | 
			
		||||
	if(!page) return el();
 | 
			
		||||
	let { href, title, description }= page;
 | 
			
		||||
	return el("a", { rel, href, title: description }).append(
 | 
			
		||||
		rel==="next" ?"(next) " : "",
 | 
			
		||||
		title,
 | 
			
		||||
		rel==="prev" ? " (previous)" : "",
 | 
			
		||||
	);
 | 
			
		||||
		if(!page) return el();
 | 
			
		||||
		let { href, title, description }= page;
 | 
			
		||||
 | 
			
		||||
		// Find the page index to show numbering
 | 
			
		||||
		const pageIndex = pages.findIndex(p => p === page);
 | 
			
		||||
		const pageNumber = pageIndex + 1;
 | 
			
		||||
 | 
			
		||||
		const linkTitle = rel === "prev"
 | 
			
		||||
				? `Previous: ${pageNumber}. ${title}`
 | 
			
		||||
				: `Next: ${pageNumber}. ${title}`;
 | 
			
		||||
 | 
			
		||||
		return el("a", {
 | 
			
		||||
				rel,
 | 
			
		||||
				href,
 | 
			
		||||
				title: description || linkTitle
 | 
			
		||||
		}).append(
 | 
			
		||||
				title
 | 
			
		||||
		);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user