Blog Post Page
by Nicholas Berlette
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/alpinejs/3.9.5/cdn.js"></script>
<script src="https://unpkg.com/@unocss/[email protected]/preset-icons.global.js"></script>
<script src="https://unpkg.com/@unocss/[email protected]/preset-typography.global.js"></script>
<script src="https://unpkg.com/@unocss/[email protected]/preset-attributify.global.js"></script>
<script src="https://unpkg.com/@unocss/[email protected]/preset-uno.global.js"></script>
<script src="https://unpkg.com/@unocss/[email protected]/preset-wind.global.js"></script>
<script>
globalThis.__unocss = {
extendTheme(theme) {
return {
...theme,
fontFamily: {
...theme.fontFamily,
mono: ["OperatorMono Nerd Font", "Operator Mono Lig", "Operator Mono ScreenSmart", "Dank Mono", "DM Mono", "IBM Plex Mono", "Mono Lisa", "ui-monospace"],
sans: ["Gotham Narrow ScreenSmart", "Gotham Narrow", "Whitney Narrow", "Whitney Narrow SSm", "Gotham Narrow SSm", "Inter", "ui-sans-serif", "AppleColorEmoji", "Apple Color Emoji"],
serif: ["Sentinel ScreenSmart", "Sentinel SSm", "Sentinel", "Bitter", "IBM Plex Serif", "Georgia", "Times New Roman", "ui-serif"],
header: ["Obsidian", "Knockout-93", "Gotham", "Whitney", "Helvetica"],
},
};
},
presets: [
() => window.__unocss_runtime.presets.presetIcons({
cdn: 'https://esm.sh/',
}),
() => window.__unocss_runtime.presets.presetUno({
variablePrefix: "tw-",
}),
() => window.__unocss_runtime.presets.presetWind({
variablePrefix: "tw-",
}),
() => window.__unocss_runtime.presets.presetTypography({
}),
() => window.__unocss_runtime.presets.presetAttributify({
}),
]
};
window.onload = () => {
document.querySelector("#toggle-theme").addEventListener("click", toggleDark);
};
</script>
<script src="https://unpkg.com/@unocss/[email protected]/core.global.js"></script>
<script type="module">
/**
* This module provides a set of isomorphic file system functions...
CSS
@layer utilities, components;
@layer components {
:root {
--bg: light-dark(#fff, #000);
--heading: light-dark(#111, #fff);
--kbd-border: light-dark(#d1d5da, #4b5563);
--primary: light-dark(#dc2626, #ef4444);
--primary-deep: light-dark(#b91c1c, #f87171);
--subtle: light-dark(#e5e7eb, #1f2937);
--fg-shallow: light-dark(#9ca3af, #6b7280);
--fg: light-dark(#374151, #d1d5db);
--fg-deep: light-dark(#1f2937, #e5e7eb);
--fg-deeper: light-dark(#111827, #f3f4f6);
--tw-bg-opacity: 1;
--button-bg-light: rgba(231, 229, 228, var(--tw-bg-opacity));
--button-bg-dark: rgba(45, 45, 45, var(--tw-bg-opacity)));
--button-bg: light-dark(var(--button-bg-light), var(--button-bg-dark));
--color-bg: var(--bg);
--color-heading: var(--heading);
--color-primary: var(--primary);
--color-primary-deep: var(--primary-deep);
--color-subtle: var(--subtle);
--color-fg-shallow: var(--fg-shallow);
--color-fg: var(--fg);
--color-fg-deep: var(--fg-deep):
--color-fg-deeper: var(--fg-deeper);
color-scheme: light dark;
background-color: var(--bg) !important;
-moz-tab-size: 2;
-o-tab-size: 2;
tab-size: 2;
}
html, body {
background-color: var(--bg) !important;
color: var(--fg);
transition: background-color 250ms ease-in, color 150ms ease-out;
}
.dark:root {
color-scheme: dark !important;
}
.light:root {
color-scheme: light !important;
}
.prose {
color: var(--color-fg-deep);
}
.prose a {
color: var(--primary-deep)
}
.prose a:hover,
.prose a:hover>kbd {
text-decoration: underline
}
.icon-next {
margin-left: .25rem
}
.icon-prev {
margin-right: .25rem
}
.icon {
display: block;
flex-shrink: 0;
width: 16px;
height: 16px;
fill: var(--fg);
transform: translateY(1px)
}
kbd {
background-color: var(--prism-background);
border: 1px solid var(--kbd-border);
...
TypeScript
// #region color schemes
type ColorScheme = "auto" | "dark" | "light";
/* toggleDark(isDark, false); */
function toggleDark<E extends { matches?: boolean }>(event: E, persisted = true): void {
const dark = "dark";
const light = "light";
const auto = "auto";
const key = "color-scheme";
const target: Element = globalThis.document.documentElement;
const list: DOMStringList | null = target?.classList;
const storageArea = globalThis[persisted ? "localStorage" : "sessionStorage"];
let setting = storageArea.getItem(key) || auto;
const prefersDark = setting === auto && (event != null && "matches" in event ? event : mql).matches;
// let value = setting === light || (prefersDark && setting !== light);
let value = prefersDark;
if (event != null && !("matches" in event)) {
// cycle to the next setting for each click.
// e.g. light -> dark, dark -> auto, auto -> light
setting = setting === light ? dark : setting === dark ? auto : light;
value = setting === dark || (setting === auto && mql.matches);
}
list?.toggle(dark, value);
list?.toggle(light, !value);
storageArea.setItem(key, setting);
const el = event.target;
const cl = el?.classList;
cl?.toggle("i-line-md:sunny-outline-to-moon-alt-loop-transition", setting === dark);
cl?.toggle("i-line-md:moon-to-sunny-outline-loop-transition", setting === light);
cl?.toggle("i-line-md:light-dark-loop", setting === auto);
}
const mql = window.matchMedia("(prefers-color-scheme: dark)");
toggleDark(mql);
mql.addEventListener("change", toggleDark);
// #endregion color schemes
// #region blog api (WIP)
class Blog {
constructor(
public posts: Blog.Post[] = [],
readonly config: Blog.Config = Blog.defaultConfig,
) {}
}
declare namespace Blog {
interface Config {
}
interface Metadata {
name: string;
icon: string;
theme: Color
}
}
Blog.defaultConfig = {
title: "Nicholas Berlette",
meta: {
name: "Nicholas Berlette",
...