---
name: nirman
description: Generate a complete, ready-to-use nirman static site project from a user's description or requirements. Use this skill whenever the user wants to scaffold, create, initialise, or build a website using the nirman static site generator — including when they ask to "create a nirman project", "generate a site with nirman", "scaffold a static site", "build a nirman website", or provide a description of a website they want created. Also triggers when the user uploads a design brief, content outline, or specification and wants a nirman project generated from it. Always use this skill rather than guessing the nirman project structure from scratch.
---

# Nirman Site Generator Skill

This skill guides the generation of a complete, working **nirman** static site project from a user's description. Nirman is a path-based static site generator with strict separation of content and design, built on Jinja2 templating.

When the user provides a description, domain, or content brief — produce a fully populated project directory tree ready to be built with `nirman build`.

---

## Core Architecture (memorise this)

```
project-root/
├── pyproject.toml
├── pages/               ← Content + page-level meta. Path = URL.
├── layouts/             ← Outer HTML shell. base.html is the default.
├── components/          ← Structural/navigational fragments used across pages.
├── scripts/
│   ├── js/              ← Large or cross-page JS files only
│   └── css/             ← Cross-page CSS, frameworks (e.g. Tailwind), or design tokens only
├── texts/               ← Raw .txt or .md content blocks
├── static/              ← Images, fonts, videos. Served at /static/<path>
└── public/              ← favicon, robots.txt, root-level files
```

Output goes to `dist/` by default.

---

## Step-by-Step Generation Process

### 1. Understand the site

Extract from the user's request:
- **Site purpose** — portfolio, blog, product landing page, docs site, etc.
- **Pages needed** — derive from the description; always include at minimum `index`
- **Content tone** — professional, playful, minimal, editorial, etc.
- **Any explicit constraints** — specific pages, colour schemes, fonts, features

If the request is vague (e.g. "make me a portfolio site"), make reasonable creative decisions and state them clearly. Do not ask unnecessary questions — generate and explain.

### 2. Plan the file tree

Before writing any files, produce a concise plan:
- List every page file and its URL path
- Identify which components are needed (structural/navigational only — see Components section)
- Decide whether any script files are needed at all — most pages carry their own inline `<style>` and `<script>` tags; only extract to `scripts/` if the content is cross-page or large (see Scripts section)
- Note which layout(s) are needed beyond `base.html`

### 3. Generate files in this order

Always generate files in this sequence to avoid forward references:

1. `pyproject.toml`
2. `layouts/base.html` (and any additional layouts)
3. `components/*.html`
4. `scripts/css/*.css` — only if cross-page CSS or a framework is needed
5. `scripts/js/*.js` — only if a large or cross-page JS file is needed
6. `texts/*.md` or `texts/*.txt` (if needed)
7. `pages/**/*` — all page files (inline `<style>` and `<script>` tags go here)
8. `public/robots.txt` (minimal stub)

---

## File Generation Rules

### pyproject.toml

Always include the `[tool.nirman]` table. Populate `[tool.nirman.site]` with values derived from the user's request:

```toml
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "my-site"
version = "0.1.0"

[tool.nirman]
source_dir = "."
output_dir = "dist"
base_url = "https://example.com"

[tool.nirman.site]
title = "Site Title"
description = "One-line site description."
language = "en"
```

### layouts/base.html

Every project **must** have `layouts/base.html`. It is the default layout for all pages.

Rules:
- Must contain `{{ page_content }}` exactly once — this is where the page fragment is injected
- Must contain `{{ scripts.css }}` inside `<head>` for CSS injection
- Must contain `{{ scripts.js }}` before `</body>` for JS injection
- May include components via `{% include 'components/nav.html' %}` etc.
- Must reference `{{ meta.title | default(site.title) }}` in `<title>`
- Use real, semantic HTML5 boilerplate — never a skeletal stub

Example structure:

```html
<!DOCTYPE html>
<html lang="{{ site.language | default('en') }}">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{ meta.title | default(site.title) }}</title>
  <meta name="description" content="{{ meta.description | default(site.description) }}">
  {{ scripts.css }}
</head>
<body>
  {% include 'components/nav.html' %}
  <main>{{ page_content }}</main>
  {% include 'components/footer.html' %}
  {{ scripts.js }}
</body>
</html>
```

### Additional layouts

If the site needs a different shell for certain pages (e.g. a full-bleed hero layout, a sidebar layout, a docs layout), create `layouts/<n>.html` following the same rules as `base.html`. Pages select it via their meta block:

```
---
layout: sidebar.html
---
```

### components/*.html

Components are Jinja HTML **fragments for structural and navigational elements that appear across multiple pages** — things like the site nav, footer, sidebar, or breadcrumb. They are explicitly **not** for repeating content items like cards, list rows, or feature blocks.

#### The golden rule

> If something repeats because of **data** (cards, rows, items), use a **Jinja loop inside the page**.
> If something repeats because it appears on **every page** (nav, footer), use a **component**.

#### ✅ Correct use of components

| Component       | Reason it belongs here                          |
|-----------------|-------------------------------------------------|
| `nav.html`      | Appears on every page via the layout            |
| `footer.html`   | Appears on every page via the layout            |
| `sidebar.html`  | Structural column used across a docs section    |
| `breadcrumb.html` | Navigational element used across a section    |
| `cookie-banner.html` | Persistent UI element included in layout  |

#### ❌ Wrong use of components — use loops instead

| Do not create this component | Do this in the page instead |
|------------------------------|-----------------------------|
| `project-card.html` × 6      | `{% for project in projects %}` loop |
| `blog-post-card.html` × 10   | `{% for post in posts %}` loop |
| `team-member.html` × 5       | `{% for member in team %}` loop |
| `pricing-tier.html` × 3      | Inline HTML with a loop or hardcoded tiers |
| `feature-block.html` × 4     | `{% for feature in features %}` loop |

#### Jinja loop pattern for repeating content

Define data inline in the page using `{% set %}` and loop over it. All markup for the repeating item lives inside the loop — no separate component file needed:

```html
---
title: Our Work
description: Selected projects.
---

{% set projects = [
  {"title": "Brand Identity", "client": "Acme Co", "year": 2024, "image": "/static/work/acme.jpg"},
  {"title": "Web Redesign",   "client": "Nova Ltd", "year": 2024, "image": "/static/work/nova.jpg"},
  {"title": "Motion System",  "client": "Flux Inc", "year": 2023, "image": "/static/work/flux.jpg"}
] %}

<section class="work-grid">
  {% for project in projects %}
  <article class="project-card">
    <img src="{{ project.image }}" alt="{{ project.title }}">
    <h2>{{ project.title }}</h2>
    <p>{{ project.client }} &middot; {{ project.year }}</p>
  </article>
  {% endfor %}
</section>
```

This keeps data co-located with the page, avoids orphaned files, and passes `nirman lint` cleanly.

#### Component rules

- **Never** include `<html>`, `<head>`, or `<body>` tags — fragments only
- May use Jinja variables and conditionals freely
- May include other components: `{% include 'components/subnav.html' %}`
- **Never** create circular includes — if A includes B, B must not include A
- Always write real, populated content — not placeholders like `<!-- links here -->`

**Typical component count by site type:**

| Site type        | Components                                     |
|------------------|------------------------------------------------|
| Portfolio        | `nav`, `footer`                                |
| Blog             | `nav`, `footer`                                |
| Product/Landing  | `nav`, `footer`                                |
| Docs             | `nav`, `footer`, `sidebar`, `breadcrumb`       |

A site with 2–4 components is normal and healthy. More than 6 components almost always means repeating content has been incorrectly extracted — refactor to loops.

### CSS and JS — self-sufficiency first

**Pages are self-sufficient by default.** Inline `<style>` and `<script>` tags inside a page file are the preferred approach for page-specific styling and behaviour. This keeps each page complete in one file and minimises the number of files an AI needs to generate and maintain.

```html
---
title: Contact
description: Get in touch.
---

<style>
  .contact-form { max-width: 600px; margin: 4rem auto; }
  .contact-form input { width: 100%; padding: .75rem; margin-bottom: 1rem; }
</style>

<section class="contact-form">
  ...
</section>

<script>
  document.querySelector('form').addEventListener('submit', (e) => { ... });
</script>
```

#### When to use scripts/css/

Only move CSS to `scripts/css/` when it meets one of these criteria:

| Criterion | Example |
|---|---|
| Used across multiple pages | A design token system or reset stylesheet |
| A CSS framework or library | Tailwind base, Bootstrap, custom utility classes |
| So large it would dominate the page file | A complex animation library, a syntax highlighter theme |

Use **kebab-case** filenames, no spaces (e.g. `main.css`, `tokens.css`). Referenced in templates as `{{ css.main }}`, `{{ css.tokens }}` (hyphens → underscores in variable name).

#### When to use scripts/js/

Only move JS to `scripts/js/` when it meets one of these criteria:

| Criterion | Example |
|---|---|
| Used across multiple pages | Analytics initialisation, a global mobile menu |
| So large it would dominate the page file | A chart library wrapper, a form validation engine (200+ lines) |
| A third-party bundle that should be cached separately | A custom-built component library |

Use **kebab-case** filenames, no spaces (e.g. `main.js`, `mobile-menu.js`). Referenced as `{{ js.main }}`, `{{ js.mobile_menu }}`.

#### The token economy rule

> **Prefer inline over file extraction.** Every file created is a file an AI must generate in full. Inline CSS/JS that is page-specific costs nothing extra — the page must be written anyway. Only extract to `scripts/` when the benefit (reuse, size isolation) clearly outweighs the cost of an additional file.

As a rough guide: if a CSS or JS block is under ~80 lines and only used on one page, keep it inline. If it exceeds ~80 lines or appears on two or more pages, extract it.

### texts/*.md or texts/*.txt

Use `texts/` for substantial prose that would clutter a page file — long about blurbs, policy text, extended bios. Reference in page templates as `{{ texts.about_blurb }}`.

Only use `texts/` when the content block is genuinely long or reused across multiple pages. Short inline content belongs directly in the page. An unused `texts/` file will be flagged as an orphan warning by `nirman lint`.

### pages/**/*

Pages are the heart of the project.

**Meta block** — every page must start with `---` on line 1:

```
---
title: Page Title
description: Page-specific description for SEO.
---
```

- `layout:` key is optional — omit it to use the default `base.html`
- Always include `title` and `description`
- Add `og_image`, `sitemap_changefreq`, `sitemap_priority`, or `draft` as needed

**Page content** — write real Jinja HTML after the meta block. Pages receive:
- `{{ meta.<key> }}` — current page's meta values
- `{{ site.<key> }}` — site-wide config from pyproject.toml
- `{{ js.<n> }}` and `{{ css.<n> }}` — script injection
- `{{ texts.<n> }}` — text imports (pages only — not available in components or layouts)

**Path → URL mapping:**

| File path                    | Rendered URL         |
|------------------------------|----------------------|
| `pages/index.html`           | `/`                  |
| `pages/about.md`             | `/about/`            |
| `pages/blog/index.html`      | `/blog/`             |
| `pages/blog/first-post.md`   | `/blog/first-post/`  |

Always create an `index.html` or `index.md` for section roots.

**Supported page formats:**

- `.html` / `.htm` — Jinja HTML, rendered as-is then wrapped in layout
- `.md` — Markdown, converted to HTML then wrapped in layout
- `.yml` / `.yaml` — Data page; pairs with a same-named Jinja template
- `<n>.<ext>.yml` — Compound extension plugin (e.g. `pricing.apps.yml`)

Use `.md` for content-heavy pages (blog posts, about, docs). Use `.html` for pages with Jinja logic, loops, or conditionals.

### public/robots.txt

Always generate a minimal `robots.txt`:

```
User-agent: *
Allow: /

Sitemap: https://example.com/sitemap.xml
```

Replace the domain with the `base_url` value from `pyproject.toml`.

---

## Script Deduplication

If multiple components or a page and its components reference the same script (e.g. both `nav.html` and `footer.html` call `{{ css.main }}`), nirman deduplicates automatically at build time. Reference scripts wherever logically needed — no manual guards required.

---

## CLI Commands Reference

After generating the project, always tell the user the correct commands in this order:

```bash
# 1. Check the project for structural errors
nirman lint

# 2. Build the project into dist/
nirman build

# 3. Preview the built site locally
nirman serve
```

`nirman lint` checks for:
- Missing layout references (layout declared in meta but file not found in `layouts/`)
- Circular component imports
- Broken `{{ js.* }}` and `{{ css.* }}` script references
- Broken `/static/<path>` URLs referenced in templates
- Orphaned files in `components/`, `scripts/`, and `texts/` (reported as warnings, not errors)

Always recommend running `nirman lint` before `nirman build` after generating a new project.

---

## Quality Checklist

Before presenting output, verify every item:

- [ ] `layouts/base.html` exists and contains `{{ page_content }}`, `{{ scripts.css }}`, `{{ scripts.js }}`
- [ ] Every page file starts with `---` on line 1
- [ ] No component was created solely to wrap a repeating content item — use loops instead
- [ ] No component includes itself or creates a circular chain
- [ ] All `{{ js.<n> }}` references have a matching file in `scripts/js/`
- [ ] All `{{ css.<n> }}` references have a matching file in `scripts/css/`
- [ ] All `{% include 'components/<n>.html' %}` references have a matching file in `components/`
- [ ] Page-specific CSS and JS is written inline in the page file, not extracted to `scripts/` unnecessarily
- [ ] Any file in `scripts/css/` or `scripts/js/` is either cross-page, a framework, or genuinely large (80+ lines)
- [ ] `pyproject.toml` has a `[tool.nirman]` table with `base_url` populated
- [ ] At least one page exists at `pages/index.html` or `pages/index.md`
- [ ] `public/robots.txt` exists
- [ ] No generated file is unreferenced (orphans trigger `nirman lint` warnings)

---

## Output Format

Present the generated project as a series of clearly labelled file blocks, in generation order:

```
### pages/work.html
---
title: Work
description: Selected projects by Aria.
---

{% set projects = [...] %}
...
```

After all files, summarise:
- Total files generated
- URL map (page file → rendered URL)
- Suggested workflow: `nirman lint` → `nirman build` → `nirman serve`
- Any notable decisions (e.g. "project cards rendered via Jinja loop in `pages/work.html`", "CSS kept inline — no `scripts/css/` needed", "extracted `scripts/js/chart.js` as it exceeded 80 lines")

---

## Example: Portfolio Site

A user says: *"Create a nirman site for a freelance designer named Aria."*

**Pages:** `index`, `work`, `about`, `contact`

**Components:** `nav`, `footer` only

**Work page:** defines a `{% set projects = [...] %}` list at the top of `pages/work.html` and renders project cards with a `{% for project in projects %}` loop — no `project-card` component created.

**Scripts:** No `scripts/` files needed — each page carries its own inline `<style>` and `<script>`. Only a `scripts/css/tokens.css` would be created if a shared design token file is warranted.

**Layouts:** `base.html` only

---

## Common Mistakes to Avoid

- **Creating a component for each repeating item** — use a Jinja loop in the page; components are for structural/navigational elements that span pages
- **Empty or stub files** — every file must have real content; no `<!-- TODO -->` placeholders
- **Extracting page-specific CSS/JS to scripts/** — keep it inline in the page; only extract when cross-page, a framework, or 80+ lines
- **Creating a script file for a small behaviour** — a 10-line scroll handler belongs inline in the page, not in `scripts/js/`
- **Script names with spaces** — use hyphens (`mobile-menu.js` not `mobile menu.js`)
- **Components with full HTML structure** — components are fragments, never full pages
- **Circular component includes** — verify the include chain has no cycles
- **Forgetting `{{ page_content }}`** in a layout — the page renders blank without it
- **Using `texts/` in components or layouts** — texts imports are available in pages only
- **Unused files** — every generated file must be referenced; orphans trigger `nirman lint` warnings