Quick Start
Quick Start
Learn how Blocks detects drift and helps maintain consistency through a real example: building resume themes with proper accessibility and design.
The Problem: Consistency Is Hard at Scale
When building many similar components (whether by humans or AI), common issues emerge:
- ❌ Text colors matching backgrounds (completely unreadable)
- ❌ Poor color contrast (fails WCAG accessibility standards)
- ❌ Broken responsive layouts (mobile unusable)
- ❌ Non-semantic HTML (terrible for screen readers)
- ❌ Missing ARIA labels (accessibility nightmare)
Blocks helps by detecting drift from your domain rules and providing specific, actionable feedback to help you maintain consistency.
Real Example: JSON Resume Themes
This example is from examples/json-resume-themes in the repo.
We'll build a resume theme and show how Blocks catches inconsistencies and drift from your domain rules.
Step 1: Define Your Domain (NOT in a blocks/ folder!)
Create blocks.yml in your project root — you don't need a special blocks/ directory:
project:
name: "JSON Resume Themes"
domain: "jsonresume.themes"
philosophy:
- "Resume themes must prioritize readability and professionalism."
- "All themes must be responsive and accessible."
- "Semantic HTML and proper structure are required."
domain:
entities:
resume:
fields: [basics, work, education, skills]
signals:
readability:
description: "How easy is the resume to read and scan?"
extraction_hint: "Look for typography, spacing, visual hierarchy"
accessibility:
description: "WCAG compliance and screen reader friendliness"
extraction_hint: "Verify semantic HTML, ARIA labels, color contrast"
measures:
valid_html:
constraints:
- "Must output valid HTML5"
accessibility_score:
constraints:
- "Must use semantic HTML5 tags"
- "Color contrast must meet WCAG AA standards"
blocks:
theme.modern_professional:
type: template
description: "Clean, modern resume theme"
path: "themes/modern-professional" # ← Your code lives here
template_engine: hbs
test_data: "test-data/sample-resume.json"
inputs:
- name: resume
type: entity.resume
outputs:
- name: html
type: string
measures: [valid_html, accessibility_score]
domain_rules:
- id: semantic_html
description: "Must use semantic HTML tags (header, main, section, article)"
- id: accessibility
description: "Must include proper ARIA labels and semantic structure"
- id: responsive_design
description: "Must use CSS media queries for responsive layout"
- id: visual_hierarchy
description: "Must establish clear visual hierarchy with typography"Flexible Project Structure: Notice the path: "themes/modern-professional" field. You can organize your code however you want! Blocks doesn't force you into a blocks/ folder. Just point to where your code lives.
Step 2: Create Your Theme Implementation
Your code can live anywhere. We'll put it in themes/modern-professional/:
import Handlebars from "handlebars";
import * as fs from "fs";
import * as path from "path";
/**
* Domain compliance enforced at development time by Blocks validator.
* Validator analyzes template.hbs source for semantic HTML, ARIA, responsiveness.
*/
export function modernProfessionalTheme(
resume: Resume,
config?: ThemeConfig
) {
// Input validation only
if (!resume.basics?.name || !resume.basics?.label) {
throw new Error("Resume must include basics.name and basics.label");
}
// Render template (template is validated by Blocks, so we trust it)
const templateSource = fs.readFileSync(
path.join(__dirname, "template.hbs"),
"utf-8"
);
const template = Handlebars.compile(templateSource);
return {
html: template(resume),
};
}
interface Resume {
basics: {
name: string;
label: string;
email?: string;
phone?: string;
summary?: string;
};
work?: Array<any>;
education?: Array<any>;
skills?: Array<any>;
}
interface ThemeConfig {
colors?: {
primary?: string;
background?: string;
};
}<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{basics.name}} - Resume</title>
<style>
/* Responsive layout */
@media (max-width: 768px) {
.container { padding: 1rem; }
}
/* WCAG AA compliant colors */
body {
color: #1a1a1a;
background: #ffffff;
}
</style>
</head>
<body>
<header role="banner">
<h1>{{basics.name}}</h1>
<p aria-label="Professional title">{{basics.label}}</p>
</header>
<main role="main">
{{#if basics.summary}}
<section aria-labelledby="summary-heading">
<h2 id="summary-heading">Summary</h2>
<p>{{basics.summary}}</p>
</section>
{{/if}}
{{#if work}}
<section aria-labelledby="experience-heading">
<h2 id="experience-heading">Experience</h2>
{{#each work}}
<article>
<h3>{{position}} at {{name}}</h3>
<p>{{summary}}</p>
</article>
{{/each}}
</section>
{{/if}}
</main>
</body>
</html>export { modernProfessionalTheme } from "./block.js";Step 3: Validate Your Theme
Run the Blocks validator:
blocks run theme.modern_professional✅ When everything is correct:
🧱 Blocks Validator
📦 Validating: theme.modern_professional
✓ schema ok (inputs/outputs match spec)
✓ shape ok (files exist, exports present)
✓ domain ok (semantic HTML, ARIA labels, responsive)
✅ Block "theme.modern_professional" passed all validations❌ When AI generates bad code:
Let's say an AI agent generates this broken template:
<!-- BAD: No semantic HTML, no ARIA, poor contrast -->
<div>
<div style="color: #ccc; background: #ddd">{{basics.name}}</div>
<div>{{basics.label}}</div>
</div>Running blocks run theme.modern_professional:
🧱 Blocks Validator
📦 Validating: theme.modern_professional
✓ schema ok
✓ shape ok
⚠ [domain] Missing semantic HTML structure
→ Found: Generic <div> tags
→ Expected: <header>, <main>, <section>, <article>
→ Rule: semantic_html
⚠ [domain] Missing ARIA labels
→ No aria-label or aria-labelledby attributes found
→ Required for screen reader accessibility
→ Rule: accessibility
⚠ [domain] Poor color contrast detected
→ Text color #ccc on background #ddd
→ Contrast ratio: 1.8:1 (WCAG AA requires 4.5:1)
→ Rule: accessibility
❌ Block "theme.modern_professional" has 3 warningsHumans Can Write Code Too!
CRITICAL: This example shows AI writing code, but humans can freely edit any file:
- ✅ You can manually edit
template.hbs - ✅ You can manually edit
block.ts - ✅ Blocks detects drift regardless of who wrote the code
- ✅ Validation helps you decide: fix code or update spec
The workflow is the same:
- Human or AI writes code
- Run
blocks run theme.modern_professional - See drift report
- Decide what to fix (code or spec)
- Re-validate until consistent
Example: Working with an AI Assistant
When you're coding with Claude Code (or Cursor, Copilot, etc.), here's how the workflow looks. Note: This same workflow applies when humans write code!
1. Someone Writes Code
You or an AI: "Create a modern resume theme with professional styling"
Code gets written (by human or AI agent).
2. Run Validation
After writing or modifying code, run validation:
blocks run theme.modern_professional3. Review Drift Report
⚠ [domain] Poor color contrast detected
→ Text color #e0e0e0 on background #ffffff
→ Contrast ratio: 1.4:1 (WCAG AA requires 4.5:1)4. Fix the Issue
Read the error, understand the domain rule, and fix it (human or AI can do this):
- color: #e0e0e0;
+ color: #1a1a1a; /* WCAG AA compliant */5. Re-Validate
blocks run theme.modern_professional✅ Block "theme.modern_professional" passed all validationsThis feedback loop is the same for humans and AI - iterative refinement guided by your domain spec, catching drift and helping maintain consistency.
Output Validators: Future Vision
In the current version, Blocks validates source code. Future versions will add output validators that render and analyze the actual HTML output.
Example: Computer Vision for Color Contrast
validators:
output:
- id: color_contrast_cv
run: "output.vision.contrast.v1"
config:
model: "gpt-4o-vision" # or Claude with vision
test_data: "test-data/sample-resume.json"This validator would:
- Render
output.htmlfrom your theme - Screenshot the page
- Use a vision model to analyze:
- Color contrast ratios
- Text readability
- Visual hierarchy
- Responsive breakpoints
Why this is powerful:
- Catches issues that are hard to detect in source (like computed CSS styles)
- Validates the actual user experience
- Works even with complex CSS frameworks
Understanding the Validators
Schema Validator (Fast, Deterministic)
- ✅ Input types match
blocks.yml - ✅ Output types match specification
- ✅ Required fields present
Shape Validator (Fast, Deterministic)
- ✅ File structure correct (
block.ts,index.ts,template.hbs) - ✅ Exports present and named correctly
Domain Validator (Slow, AI-Powered)
- ✅ Reads ALL files in your block (not just block.ts)
- ✅ Analyzes template source for semantic HTML
- ✅ Checks domain rules compliance
- ✅ Validates against philosophy statements
- ✅ Detects undocumented concepts (drift detection)
Key insight: The domain validator reads template.hbs source, not rendered output. It analyzes the code with AI to ensure semantic correctness.
What If I Don't Use a blocks/ Folder?
You don't have to! Blocks respects your project structure:
blocks:
theme.modern_professional:
path: "themes/modern-professional" # ← Custom path
theme.creative:
path: "src/themes/creative" # ← Anywhere you want
utils.format_date:
path: "lib/utilities/date-formatter" # ← Whatever structureIf you don't specify path, Blocks defaults to blocks/<block-name>/ (or whatever you set in targets.discover.root).