Put everything together. Build a complete developer portfolio from scratch using layouts, components, loops, conditionals, filters, markdown, and deploy it live.
We'll build a developer portfolio website with:
<script data>portfolio/
├── orb.config.json
├── package.json
├── layouts/
│ └── main.layout
├── components/
│ ├── nav.orb
│ ├── hero.orb
│ ├── project-card.orb
│ ├── skill-badge.orb
│ └── footer.orb
├── pages/
│ ├── index.orb # Homepage
│ ├── projects.orb # Projects page
│ └── blog/
│ └── hello-world.orb # Blog post
├── assets/
│ └── style.css
└── dist/ # ← Generated output
mkdir portfolio && cd portfolio
npm init -y
npm install orblayout
npx orb init
Update your package.json scripts:
{
"scripts": {
"dev": "orb dev",
"build": "orb build",
"prod": "orb clean && orb build --minify"
}
}
This layout wraps every page with a consistent HTML shell, nav, and footer.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport"
content="width=device-width, initial-scale=1.0" />
<title>{{ title }} — Portfolio</title>
<link rel="stylesheet"
href="/assets/style.css" />
{{ head }}
</head>
<body>
<use component="nav" active="{{ navActive }}" />
<main>
{{ content }}
</main>
<use component="footer" />
</body>
</html>
Named slot: {{ head }} lets each page inject custom meta tags or styles into <head>.
<nav class="site-nav">
<a href="/" class="logo">◉ Dan</a>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/projects">Projects</a></li>
<li><a href="/blog/hello-world">Blog</a></li>
</ul>
</nav>
<section class="hero">
<h1>{{ heading }}</h1>
<p>{{ subheading }}</p>
{{#if cta}}
<a href="{{ ctaLink }}" class="btn">{{ cta }}</a>
{{/if}}
</section>
<style>
.project-card {
background: #111;
border-radius: 12px;
padding: 1.5rem;
border: 1px solid #222;
transition: transform 0.2s;
}
.project-card:hover { transform: translateY(-4px); }
.project-card h3 { margin: 0 0 0.5rem; }
.project-card .tag {
display: inline-block;
background: #6c5ce7;
color: #fff;
padding: 2px 10px;
border-radius: 99px;
font-size: 0.75rem;
}
</style>
<div class="project-card">
<h3>{{ name }}</h3>
<p>{{ description }}</p>
<span class="tag">{{ tech }}</span>
{{#if url}}
<a href="{{ url }}">View →</a>
{{/if}}
</div>
<span class="skill-badge">{{ name }}</span>
<footer class="site-footer">
<p>© {{ @page.year }} Dan — Built with
<a href="https://github.com/rukkit-official/orblayout">OrbLayout</a>
</p>
</footer>
The homepage brings it all together: data, components, loops, conditionals, and filters.
<script data>
({
title: "Home",
navActive: "home",
name: "Dan",
role: "full-stack developer",
skills: [
"JavaScript", "Node.js", "React",
"Python", "CSS", "OrbLayout"
],
featuredProjects: [
{
name: "OrbLayout",
description: "A lightweight static site builder",
tech: "Node.js",
url: "https://github.com/rukkit-official/orblayout"
},
{
name: "TaskFlow",
description: "Minimal task management app",
tech: "React",
url: ""
}
]
})
</script>
<layout src="main.layout">
<content>
<!-- Hero using a component -->
<use component="hero"
heading="Hi, I'm {{ name }}"
subheading="I'm a {{ role | capitalize }}"
cta="View Projects"
ctaLink="/projects" />
<!-- Skills — loop over primitives -->
<section class="skills">
<h2>Skills</h2>
<div class="skill-list">
{{#each skills}}
<use component="skill-badge"
name="{{ this }}" />
{{/each}}
</div>
</section>
<!-- Featured Projects — loop over objects -->
<section class="featured">
<h2>Featured Projects</h2>
<div class="projects-grid">
{{#each featuredProjects}}
<use component="project-card"
name="{{ name }}"
description="{{ description }}"
tech="{{ tech }}"
url="{{ url }}" />
{{/each}}
</div>
</section>
</content>
</layout>
Spot the features: Data block, layout, 3 components, 2 loops, filters (capitalize), conditional ({{#if url}} inside project-card), and @page.year in the footer.
<script data>
({
title: "Projects",
navActive: "projects",
projects: [
{
name: "OrbLayout",
description: "Lightweight static site builder with components, layouts, and zero config",
tech: "Node.js",
url: "https://github.com/rukkit-official/orblayout",
featured: true
},
{
name: "TaskFlow",
description: "Minimal task management app with drag-and-drop",
tech: "React",
url: "",
featured: false
},
{
name: "PixelSnap",
description: "Screenshot annotation tool",
tech: "Electron",
url: "https://pixelsnap.dev",
featured: true
}
]
})
</script>
<layout src="main.layout">
<content>
<h1>{{ title }}</h1>
<div class="projects-grid">
{{#each projects}}
<use component="project-card"
name="{{ name }}"
description="{{ description | truncate }}"
tech="{{ tech }}"
url="{{ url }}" />
{{/each}}
</div>
<p>Showing {{ projects | length }} projects</p>
</content>
</layout>
<script data>
({
title: "Hello World",
navActive: "blog",
author: {
name: "Dan",
role: "Creator"
}
})
</script>
<layout src="main.layout">
<slot:head>
<meta property="og:title"
content="{{ title }}" />
</slot:head>
<content>
<article class="blog-post">
<h1>{{ title }}</h1>
{{#with author}}
<p class="byline">
By {{ name }}, {{ role }}
</p>
{{/with}}
<time>Published {{ @page.date }}</time>
<markdown>
## Why I built OrbLayout
I wanted a static site builder that felt like
writing **real HTML**, not a framework.
### The problems with existing tools
- Too much JavaScript
- Complex build configs
- Templates that don't look like HTML
- Slow builds
### How OrbLayout is different
OrbLayout uses `.orb` files that are just
HTML with superpowers:
- **Layouts** for shared page shells
- **Components** for reusable UI blocks
- **Loops** and **conditionals** for dynamic content
- **Filters** for text transformation
- **Markdown** for content-heavy pages
- **Zero config** to get started
---
Thanks for reading! Star us on
[GitHub](https://github.com/rukkit-official/orblayout).
</markdown>
</article>
</content>
</layout>
Features used: Data block, layout, named slot (slot:head), {{#with}}, @page.date, <markdown> block. Six features in one page!
/* Reset & base */
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: system-ui, sans-serif;
background: #0a0a1a;
color: #e0e0e0;
line-height: 1.6;
}
/* Navigation */
.site-nav {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
border-bottom: 1px solid #1a1a2e;
}
.site-nav ul { list-style: none; display: flex; gap: 1.5rem; }
.site-nav a { color: #a0a0b0; text-decoration: none; }
.site-nav a:hover { color: #6c5ce7; }
/* Hero */
.hero {
text-align: center;
padding: 6rem 2rem;
}
.hero h1 { font-size: 3rem; }
.hero .btn {
display: inline-block;
margin-top: 1.5rem;
padding: 0.75rem 2rem;
background: #6c5ce7;
color: #fff;
border-radius: 8px;
text-decoration: none;
}
/* Skills */
.skill-list { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.skill-badge {
background: #1a1a2e;
padding: 6px 16px;
border-radius: 99px;
font-size: 0.85rem;
border: 1px solid #2a2a4e;
}
/* Projects grid */
.projects-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1.5rem;
padding: 2rem 0;
}
/* Blog */
.blog-post { max-width: 680px; margin: 0 auto; padding: 3rem 2rem; }
.byline { color: #888; margin: 0.5rem 0; }
/* Footer */
.site-footer {
text-align: center;
padding: 2rem;
border-top: 1px solid #1a1a2e;
color: #666;
}
.site-footer a { color: #6c5ce7; }
npm run prod
# Output:
# ✓ index.orb → index.html
# ✓ projects.orb → projects.html
# ✓ blog/hello-world.orb → blog/hello-world.html
# ✓ Assets copied
# Done! 3 page(s) compiled in 18ms
Your dist/ folder now contains pure HTML, CSS, and assets — ready to deploy anywhere.
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npm install
- run: npx orb build --minify
- uses: peaceiris/actions-gh-pages@v3
with:
publish_dir: ./dist
[build]
command = "npx orb build --minify"
publish = "dist"
{
"buildCommand": "npx orb build --minify",
"outputDirectory": "dist"
}
Build command: npx orb build --minify
Build output: dist
Node version: 20
It's just static files! OrbLayout outputs pure HTML/CSS/JS — no server-side runtime needed. Deploy to any static hosting provider.
Congratulations! You've completed the entire OrbLayout course. Here's what you now know:
| Lesson | Feature | Status |
|---|---|---|
| 01 | Installation, scaffolding, project structure, build pipeline | ✅ |
| 02 | Layouts, {{ content }}, named slots, multiple layouts | ✅ |
| 03 | Components, props, nesting, styles, imports & aliases | ✅ |
| 04 | Data blocks, variables, types, nested objects, @page.* | ✅ |
| 05 | Loops, {{ this }}, loop helpers, components in loops | ✅ |
| 06 | Conditionals, {{#if}}, {{else}}, {{#unless}}, truthy/falsy | ✅ |
| 07 | All 12 built-in filters, chaining, custom filters API | ✅ |
| 08 | Markdown blocks, {{#with}}, partials, :class directives | ✅ |
| 09 | CLI commands, dev server, config, npm scripts, programmatic API | ✅ |
| 10 | Built a complete portfolio with deployment configs | ✅ |
You've completed the OrbLayout Learning Course!
You're now equipped to build any static website with OrbLayout — from personal blogs and portfolios to documentation sites and landing pages.
After this lesson you can: