How SalienceNotes.dev Is Built
A technical deep dive into the architecture, content model, styling, and deployment pipeline of this site.
Contents
Category
Article
Tags
Contents
This post provides a complete, end-to-end technical overview of the architecture, folder structure, content model, pages, styling, integrations, build processes, and security posture of SalienceNotes.dev.
1. Project Overview
SalienceNotes.dev is a personal technical blog focused on cybersecurity, cloud platforms, AI security engineering, risk, and practical field notes. It is designed to share high-yield technical insights, lessons learned, and actionable takeaways from my work as a security engineer and architect.
The site is built as a static site using the Astro (v6) framework. Astro was chosen because of its “zero-JavaScript-by-default” footprint, which optimizes loading speed and delivery, while still permitting dynamic client-side enhancements where needed.
2. Folder and File Structure
The project is structured under an astro/ subdirectory. Here are the key folders and files:
src/: The core source code directory.assets/: Local images and media.components/: Reusable Astro components (e.g., headers, footers, theme toggles, share buttons, sidebars).content/: Content collections (foldersblog/andprojects/).layouts/: Shared page templates. The main template isBlogPost.astro.lib/: Local helper utilities (e.g., thereading-time.tsutility).pages/: File-based routing definitions (Home, Writing page, About page, 404 page, and RSS feed).styles/: Custom styling, housingglobal.css.
tests/: Contains Playwright end-to-end (E2E) tests.astro.config.mjs: Framework integration configuration.tsconfig.json: TypeScript rules configuration.package.json: Lists dependencies, devDependencies, and node engine requirements.
3. Content Model
The content model leverages Astro’s native Content Collections.
Collection Schema
The blog collection is configured in src/content.config.ts using the Zod validation library. The schema validates the following frontmatter properties:
title: String.description: String.pubDate: Date (coerced format).updatedDate: Date (optional).draft: Boolean (defaults tofalse).featured: Boolean (defaults tofalse).tags: Array of strings (defaults to[]).heroImage: Image path (optional, validated using Astro’simage()helper).
Publishing & Flow
- Drafts: Blog posts with
draft: trueare filtered out during static page generation and RSS generation. - Homepage Integration: The homepage (
src/pages/index.astro) automatically queries the blog collection, filters out drafts, and displays a link to the single most recent article in the “Latest Field Note” banner. - Writing Page: The Writing page (
src/pages/blog/index.astro) queries, sorts, and filters the collection. It extracts tags, count maps the primary topics, and populates the categories list on the sidebar. It uses both server-side logic (for default rendering based on URL parameters) and client-side JavaScript enhancements (for zero-latency list updates when switching categories or sorting orders). - Reading Time: Calculated dynamically at build time using the
reading-timelibrary viasrc/lib/reading-time.ts.
4. Pages and Routing
Astro’s file-system routing defines the site’s path structure:
- Home (
/): Handled bysrc/pages/index.astro. Displays the site’s definition of “Salience” and a call-to-action redirecting readers to Writing or About. - Writing (
/blog): Handled bysrc/pages/blog/index.astro. Houses the blog post index, sorting controls, and category filter sidebar. - About (
/about): Handled bysrc/pages/about.astro. Displays my professional profile, core role, and technical specification checklist. - Blog Posts (
/blog/[...slug]): Dynamic route defined bysrc/pages/blog/[...slug].astro. UsesgetStaticPaths()to pre-render all posts based on their IDs from the content collection. - 404 (
/404): Custom error page (src/pages/404.astro). - RSS Feed (
/rss.xml): Generated dynamically bysrc/pages/rss.xml.js.
Navigation uses the Header.astro and HeaderLink.astro components, which dynamically highlight active links based on the current URL.
5. Components
The site is built with highly modular, reusable Astro components:
Header.astro/Footer.astro: Provides site-wide structural layout, brand mark (SN), and navigation links.ThemeToggle.astro: Handles theme switches using an inline script that setsdata-themeon thehtmlroot and persists user choices inlocalStorage.blog/BlogPostHeader.astro: Renders article metadata (dates, reading times, title, and description) on post pages.blog/BlogPostSidebar.astro: Renders categories, tags, and a Table of Contents (TOC). In mobile view, this transitions into a<details>disclosures layout.blog/BlogTocList.astro: Renders the dynamic lists of headings for the TOC.blog/BlogShare.astro: Renders share options for X (Twitter), LinkedIn, and a native clipboard link copying button.Ethos.astro: Included in layout templates to display the site’s publishing ethos.
These components compile down to static HTML, with small inline scripts providing interactivity where needed.
6. Styling and Design System
The site’s visual theme adopts a minimal, publication-style layout inspired by printed editorial media.
- Vanilla CSS: Styled exclusively with a single global stylesheet (
src/styles/global.css) without relying on Utility-first frameworks like Tailwind CSS. - Color Palettes: Features curated light and dark color schemes. The light mode uses a soft, paper-like background (
#f7f5ef), dark slate borders, and dark gray text (#111827). The dark mode maps to deep navy slate colors (#0f141b/#151c25) and crisp off-white text (#eef2f7). - Fluid Typography: Responsive typography scales are declared at the
:rootlevel using CSSclamp()functions (ranging from--step--1at0.875remup to--step-5at4rem), which automatically scale with viewport width without requiring complex media query overrides. - Spacing: A strict, standardized spacing scale (from
--space-1to--space-8) keeps vertical rhythm consistent across margins, paddings, and grid layouts. - Visual details: Uses micro-animations for theme toggles, underline offsets, and subtle borders. High contrast is maintained site-wide to align with accessibility standards.
7. Integrations and Features
The site remains lean by limiting integrations to core needs:
- MDX support (
@astrojs/mdx): Allows components and HTML structures to be embedded inside blog posts (e.g.<Callout>). - Sitemap Generator (
@astrojs/sitemap): Automatically creates/sitemap-index.xmlupon compilation to aid SEO indexing. - RSS feed (
@astrojs/rss): Publishes updates at/rss.xmlfor readers using RSS feed readers. - Open Graph (OG) and Twitter Cards: Injected via
BaseHead.astrousing standard<meta>properties to ensure link previews render correctly on social channels. - Table of Contents (TOC): An inline script in
BlogPost.astroutilizes anIntersectionObserverto track the user’s scroll position and highlight the active heading in the sidebar TOC in real-time.
8. Build and Development Setup
Development is governed by npm scripts defined in package.json:
npm run dev: Starts a local development server on port4321.npm run build: Compiles the site into static HTML/CSS assets in thedist/directory.npm run check: Runs Astro and TypeScript type-checking (astro check) to catch syntax or type errors before building.npm run preview: Starts a local web server serving the builtdist/directory to simulate the production build.npm run test:e2e: Runs end-to-end tests using the Playwright framework.
Testing Setup
The codebase includes Playwright testing (tests/e2e/) verifying:
- Site-wide navigation flows.
- Responsive design styling across viewports.
- Correct loading and rendering of articles, sorting mechanisms (Oldest vs. Newest), category filtering sidebars, and the functional link-copying clipboard helper.
- Absence of 404 errors for discovered internal links.
9. Deployment Readiness
The output format is static assets (dist/), making it highly portable.
- Hosting Assumptions: Easily deployed on platforms like Cloudflare Pages, GitHub Pages, or Netlify. Since it’s pure static files, standard edge delivery can be leveraged without needing a Node.js runtime.
- Environment Variables: The site is designed to run without runtime environment variables. The E2E tests reference
process.env.BASE_URLto override the base URL if testing an active deployment. - Risks/Gaps:
- Dynamic client-side features (such as sorting and category filtering) require JavaScript. While the backend templates do support server-side rendering parameters, on a static deployment, switching pages is required if JS is disabled.
10. Security and Maintainability
From an operational perspective, the static nature of the blog provides a robust security posture:
- No Origin Servers: No runtime databases, server-side code execution, or administrative portals exist to be exploited, removing the main attack vectors common to platforms like WordPress.
- Local Testing: Playwright E2E tests act as a regression guard, catching broken links, broken layouts, or scripting errors before deployment.
- Strict Node Engine: The codebase locks Node.js version requirements (
>=22.12.0) to avoid version mismatches across deployment environments.
Potential Improvements
To further mature the setup, I would recommend:
- Security Headers: Configuring a strong Content Security Policy (CSP),
X-Frame-Options, andPermissions-Policyvia the hosting provider’s edge rules (e.g., a_headersfile for Cloudflare Pages). - Automated Link Checking: Integrating a post-build command-line link checker to flag broken external links automatically.
- Dependency Automation: Introducing dependabot to keep package dependencies (especially framework dependencies) updated.
Lessons Learned
- Pragmatic Tooling: Astro v6 fits the requirements of static blogging perfectly. It separates content from layout, compile-checks markup, and preserves speed.
- Simple JS goes a long way: Using native browser features like
IntersectionObserverfor the table of contents and native web APIs (like the clipboard API for sharing) is fast and keeps bundle sizes minimal. - End-to-End Tests for Blogs: Having automated E2E tests for a personal blog might seem like overkill, but it guarantees that adding posts or tweaking styles will not break layout, dark mode, or links.
What I Would Improve Next
- Dynamic Search: Adding a client-side search indexing tool (like Pagefind) to allow real-time full-text search across all articles.
- Automated Image Optimization: Enforcing WebP/AVIF conversions via Astro’s image optimization pipeline for all post assets.
Confidentiality Note
This review is conducted entirely on the public structure of this blog repository. It contains no client data, proprietary credentials, or private credentials.