@md/cf-page
An opinionated, HTML-close web framework for creating Cloudflare Pages with full local development support.
Getting Started
Ensure you have Deno installed and then run:
deno run -A jsr:@md/cf-page init This will:
- Prompt you for a domain name
- Download the starter template
- Extract it to a new directory
- Show you links to create a GitHub repository and set up Cloudflare Pages
Features
⚡ TypeScript Transpilation & Bundling
Automatic TypeScript compilation with full bundling support using Deno's native bundler.
Features:
- Automatic TypeScript to JavaScript transpilation
- Module bundling with dependency resolution using data URI encoding
- Minification enabled automatically in production builds
- Support for both local and remote imports (JSR, npm, HTTP)
- Type checking during build process
- Self-contained builds with base64 encoded dependencies
How it works:
- Transpilation: TypeScript files are compiled to modern JavaScript
- Bundling: All dependencies are resolved and bundled into a single file
- Remote imports: External dependencies are fetched and included in the bundle
- Production optimization: Automatic minification reduces file size for production builds
Example
// src/main.ts
import { serve } from "https://deno.land/std/http/server.ts";
import { helper } from "./utils.ts";
export default function() {
return helper("Hello TypeScript!");
}This TypeScript file will be automatically transpiled, bundled with its dependencies, and served as optimized JavaScript.
🎨 Tailwind CSS
Built-in Tailwind CSS 4 support for utility-first styling. See the Tailwind documentation for complete class reference and usage patterns.
Features:
- Tailwind CSS 4 integration
- Automatic class detection and purging
- Custom component layers support
- Dark mode variants
🗜️ File Minification
Intelligent file optimization that automatically reduces file sizes in production builds while preserving functionality and maintaining development-friendly source code.
Production-Only Minification:
- CSS: PostCSS with cssnano plugin for optimal compression
- JavaScript: Built-in minification via Deno's bundler
- TypeScript: Compiled and minified during bundling process
CSS Minification Details:
- Removes unnecessary whitespace and comments
- Optimizes CSS rules and declarations
- Merges identical selectors and rules
- Shortens color values and units where possible
- Only runs in production builds to maintain readable development code
JavaScript Minification:
- Variable name shortening and code compression
- Dead code elimination removes unused functions
- Integrated with the bundling process for optimal results
- Preserves source functionality while reducing bundle size
Development vs Production
// Development: Readable, unminified code
.header {
background-color: #ffffff;
padding: 1rem 2rem;
}
// Production: Minified and optimized
.header{background:#fff;padding:1rem 2rem}Minification only occurs during production builds, keeping your development experience fast and debuggable.
🔄 Automatic Cache-Busting
Intelligent cache-busting system that automatically adds version parameters to asset URLs, ensuring users always receive the latest versions while enabling aggressive browser caching.
Supported Elements:
- Scripts:
<script src="...">tags - Stylesheets:
<link rel="stylesheet">tags - Images:
<img src="...">tags - Media:
<video>,<audio>,<source>,<track>elements
Hash Generation Process:
- Content-based hashing using file contents for accuracy
- Base64URL encoding for URL-safe hash values
- Handles relative (`./`, `../`) and absolute (`/`) paths
- Only processes local assets, skips external URLs
- Rehype plugin integration for reliable HTML processing
Before and After
<!-- Before cache-busting -->
<link rel="stylesheet" href="/style.css">
<script src="./main.js"></script>
<img src="../images/logo.png" alt="Logo">
<!-- After cache-busting -->
<link rel="stylesheet" href="/style.css?v=abc123def456">
<script src="./main.js?v=xyz789uvw012"></script>
<img src="../images/logo.png?v=mno345pqr678" alt="Logo">Query parameters are automatically added based on file content hashes. When files change, new hashes ensure cache invalidation.
Benefits:
- Aggressive Caching: Set long cache headers without fear of stale content
- Instant Updates: Content changes immediately invalidate caches
- CDN Optimization: Perfect compatibility with CDN caching strategies
- Zero Configuration: Works automatically on all supported asset types
📄 Compile-time HTML Templating
Advanced slot-based templating system with layout inheritance that processes templates at build time, ensuring zero runtime overhead and optimal performance.
Core Features:
- Layout Inheritance: Hierarchical
+layout.htmlfiles - Named Slots: Content injection with
<slot name="..."> - Default Content: Fallback content when slots aren't filled
- Directory-based: Automatic layout discovery walking up the file tree
- Compile-time Processing: Templates resolved during build, not runtime
Layout Structure Example
<!-- src/+layout.html -->
<!DOCTYPE html>
<html>
<head>
<title><slot name="title">Default Title</slot></title>
</head>
<body>
<header><slot name="header"></slot></header>
<main><slot>Default main content</slot></main>
<footer><slot name="footer">© {cf:year}</slot></footer>
</body>
</html>Page Content Example
<!-- src/about/index.html -->
<slot name="title">About Us</slot>
<slot name="header">
<nav><a href="/">Home</a></nav>
</slot>
<h1>About Our Company</h1>
<p>This content goes into the default slot.</p>How it Works:
- Layout Discovery: Walks up directory tree to find nearest +layout.html
- String Processing: Processes templates before HTML parsing to preserve structure
- Slot Injection: Replaces
elements with matching content - Default Handling: Uses slot default content when no replacement is provided
- Build-time Resolution: All templates fully resolved during build process
Benefits:
- Zero Runtime Cost: Templates compiled away during build
- SEO Friendly: Generates complete, static HTML files
- Maintainable: Shared layouts reduce code duplication
- Flexible: Named slots allow precise content placement
🌍 Translation Engine
Built-in multi-language support with YAML translation files, parameter interpolation, and conditional rendering capabilities.
Features:
- YAML-based translation files (
+lang.yml) - Parameter interpolation with flexible syntax
- Markdown support: Use
{@md key}directive for formatted content - Conditional rendering:
{#if condition}and{#else}blocks - Array iteration:
{#each array as item}loops - Cascading translation override system
- Auto-detection of supported languages from root
+lang.yml - Global and page-specific translations
- Configuration via
$defaultLanguagekey in language files - Multi-language Build: Generates separate versions for each language (e.g.,
/en/,/de/)
Parameter Syntax
Translation values can include parameters that are replaced at build time. The syntax supports multiple parameter types:
<!-- Basic syntax: {key, param1: value1, param2: value2} -->
<!-- String literal parameters -->
<h1>{greeting, name: "John Doe"}</h1>
<!-- Output: Hello John Doe! -->
<!-- Translation key references -->
<h1>{welcome, name: {siteName}}</h1>
<!-- Output: Welcome to My Site! -->
<!-- Nested translation keys -->
<footer>{footer.copyright, year: {cf:year}}</footer>
<!-- Output: Copyright © 2025 mdressler. All rights reserved. -->
<!-- Mixed parameter types -->
<p>{message, user: {userName}, time: "10:30 AM", date: {cf:year}}</p>
<!-- Output: User John Doe logged in at 10:30 AM on 2025 -->
<!-- Built-in variables -->
<div lang="{cf:lang}">{cf:path}</div>
<!-- Output: <div lang="en">/about.html</div> -->Parameter Types:
- String literals: Enclosed in double quotes:
"value" - Translation keys: Enclosed in braces:
{key}or{nested.key} - Built-in variables:
{cf:lang}(current language),{cf:path}(current path),{cf:year}(current year)
More Parameter Examples
The parameter syntax is flexible and supports complex use cases:
<!-- Language file with nested structure -->
# src/+lang.yml
en:
messages:
greeting: "Hello {user}, welcome to {app}!"
footer:
copyright: "© {year} {company}. All rights reserved."
products:
price: "${amount} USD"
<!-- Using nested keys with parameters -->
<h1>{messages.greeting, user: "Alice", app: {siteName}}</h1>
<!-- Output: Hello Alice, welcome to My Site! -->
<!-- Multiple levels of nesting -->
<footer>{footer.copyright, year: {cf:year}, company: "ACME Corp"}</footer>
<!-- Output: © 2025 ACME Corp. All rights reserved. -->
<!-- Combining with conditionals -->
{#if isOnSale}
<span class="price">{products.price, amount: "49.99"}</span>
{#else}
<span class="price">{products.price, amount: "99.99"}</span>
{/if}Conditional & Loop Syntax
Use conditional blocks and loops to show or hide content and iterate over arrays:
<!-- Conditional rendering based on translation values -->
{#if userLoggedIn}
<p>Welcome back, {userName}!</p>
<a href="/logout">Logout</a>
{#else}
<p>Please log in to continue.</p>
<a href="/login">Login</a>
{/if}
<!-- Array iteration with markdown support -->
{#each menuItems as item}
<li>
<a href="/{item}">{navigation[item]}</a>
<!-- Use @md directive to process markdown in array items -->
<p>{@md item} section with [additional info](/{item}/info)</p>
</li>
{/each}
<!-- Conditional content based on feature flags -->
{#if featureEnabled}
<div class="new-feature">
<h3>🎉 New Feature Available!</h3>
<p>{newFeatureDescription}</p>
</div>
{/if}Note: Conditional blocks work with any translation key that resolves to a truthy/falsy value. Loop blocks work with arrays. To enable markdown processing within these blocks, use the {@md key} directive (see Markdown Processing section below).
Language Files
Create a +lang.yml file in your source directory to define languages and translations:
# src/+lang.yml
$defaultLanguage: en
global:
siteName: "My Site"
userLoggedIn: false
featureEnabled: true
menuItems: ["home", "about", "contact"]
en:
welcome: "Welcome to {name}!"
userName: "John Doe"
newFeatureDescription: "Try our new dashboard with enhanced analytics!"
navigation:
home: "Home"
about: "About"
contact: "Contact"
de:
welcome: "Willkommen bei {name}!"
userName: "Johann Müller"
newFeatureDescription: "Probieren Sie unser neues Dashboard mit erweiterten Analysen!"
navigation:
home: "Startseite"
about: "Über uns"
contact: "Kontakt"Markdown Processing
Process markdown-formatted content from translation values using the {@md key} directive:
<!-- Language file content -->
# src/+lang.yml
en:
boldText: "This is **very important**"
linkText: "Visit [our website](https://example.com)"
codeExample: "Use the `npm install` command"
richContent: "This has **bold** and `code` and [links](/) all together"
<!-- HTML template -->
<!-- Regular placeholders - markdown NOT processed -->
<p>{boldText}</p>
<!-- Outputs: This is **very important** -->
<!-- Markdown directive - markdown IS processed -->
<p>{@md boldText}</p>
<!-- Outputs: This is <strong>very important</strong> -->
<p>{@md linkText}</p>
<!-- Outputs: Visit <a href="https://example.com">our website</a> -->
<p>Command: {@md codeExample}</p>
<!-- Outputs: Command: Use the <code>npm install</code> command -->
<div>{@md richContent}</div>
<!-- Outputs: This has <strong>bold</strong> and <code>code</code> and <a href="/">links</a> all together -->Important: Markdown processing only occurs when using the {@md key} directive. Regular translation placeholders like {key} will output raw markdown text without processing. This explicit approach gives you full control over when markdown formatting is applied.
Using Translations in HTML
<!-- src/index.html -->
<h1>{welcome, name: {siteName}}</h1>
<!-- Conditional user interface -->
{#if userLoggedIn}
<p>Welcome back, {userName}!</p>
<a href="/logout">Logout</a>
{#else}
<p>Please log in to continue.</p>
<a href="/login">Login</a>
{/if}
<!-- Dynamic navigation with arrays -->
<nav>
<ul>
{#each menuItems as item}
<li>
<a href="/{item}">{navigation[item]}</a>
<!-- Use @md directive to process markdown formatting -->
<small>{@md item} section</small>
</li>
{/each}
</ul>
</nav>
<!-- Feature flag example -->
{#if featureEnabled}
<div class="feature-banner">
<h3>🎉 New Feature Available!</h3>
<p>{newFeatureDescription}</p>
</div>
{/if}🖼️ Automatic Image Optimization
Intelligent image processing for optimal web delivery without quality loss.
Optimizations:
- Lossless PNG compression
- JPG quality optimization
- SVG minification and cleaning
- Automatic format selection
📦 Inline Assets
Inline external resources directly into your HTML for reduced HTTP requests and faster page loads.
Inline Capabilities:
- SVG Images:
<img inline src="logo.svg" />- Inlines SVG as inline SVG element - Scripts:
<script inline src="script.js"></script>- Inlines JavaScript content - Stylesheets:
<link inline rel="stylesheet" href="style.css" />- Inlines CSS as