Eleventy 搭配 Nunjucks 模板引擎,输出静态 HTML
eleventy-starter — Eleventy 启动器模板
作者 Osvalam86
基于 Eleventy 与 Tailwind CSS v4 的静态站点启动器,使用 Nunjucks 模板、CSS 令牌分层和原生 JS 组件,附带开发与构建脚本。
看看你的网站会是什么样
浏览真实页面截图,比较内容排版与手机效果。点击下方缩略图切换,放大查看整页细节。
全部页面与视图 3
截图用于了解视觉与布局,交互效果请以实际项目为准。
值得关注的模板亮点
从已有项目资料中,快速了解它的特点。
Tailwind CSS v4 配合 primitive 与 theme 两层 CSS 令牌
内置 Button、Card、Dialog 三个 Nunjucks 宏组件
原生 JS 实现 dialog、dropdown、menu、tabs 交互组件
提供 dev、build、clean 与 Prettier 格式化脚本
模板详细介绍
这是一个面向静态 HTML 项目的 Eleventy 启动器,模板引擎为 Nunjucks,样式层使用 Tailwind CSS v4,交互组件用原生 JavaScript 实现。仓库提供开发与构建脚本:dev 并行运行 Eleventy 与 Tailwind CLI 并带热重载,build 先处理 CSS 再生成 HTML 到 dist/,另有 clean 与 Prettier 格式化命令。项目采用 pnpm 作为包管理器。
样式架构分为两层:primitive.css 在 :root 中定义原始颜色与尺度值,theme.css 在 @theme 中定义语义令牌,Tailwind 据此生成对应工具类。组件样式直接写在 .njk 宏中,通过 Nunjucks 映射表返回 Tailwind 类名,不使用 @apply 或独立组件 CSS 文件。
页面组织方式为每个页面一个子目录,包含 index.njk 与同名的 index.11tydata.json,后者通过 Eleventy 数据级联控制布局、永久链接、标题与内容变量。内置宏包括 Button、Card 与 Dialog,Dialog 使用 {% call %} 传入主体内容,触发与关闭通过 data-component 属性配合原生 JS 组件(dialog、dropdown、menu、tabs)实现。仓库未提供依赖清单核对,因此不列出具体版本号。
使用限制:模板为静态站点生成器,不包含服务端逻辑;演示图片、字体与图标等第三方素材需按各自许可单独核对;域名、托管等外部服务可能产生费用。
适合你的项目吗?
适用场景
- 搭建以静态 HTML 输出为主的多页面站点
- 需要以 CSS 令牌统一管理颜色与排版尺度的项目
- 希望用 Nunjucks 宏复用按钮、卡片与对话框的页面开发
- 使用 pnpm 并计划部署到 Netlify、Vercel 等静态托管平台
适合人群
- 熟悉 Nunjucks 与 Tailwind CSS 的前端开发者
- 需要轻量静态站点骨架而非完整 CMS 的项目
- 接受在模板文件中直接书写 Tailwind 类名的团队
上手与定制指南
结合仓库当前版本的说明,按下面的资料检查内容、配置与运行要求。
- 1
克隆仓库后执行 pnpm install 安装依赖
- 2
运行 pnpm run dev 启动开发服务器,默认地址为 localhost:8080
- 3
运行 pnpm run build 生成静态文件到 dist/ 目录
- 4
调整品牌色与字体时,修改 primitive.css 中的原始值并同步更新 theme.css 的语义引用
授权与来源资料
使用条件与限制
免费使用 · 保留许可声明本次收录的是公开仓库中采用 MIT 许可的免费版本;免费范围为该版本代码,不代表外部服务或第三方素材免费。
使用时需要遵守
- 允许免费使用、修改、商用和分发该版本代码。
- 分发代码副本或实质性部分时,保留原作者版权声明和完整 MIT 许可声明。
- 代码按现状提供;担保与责任限制以原许可证及适用法律为准。
额外费用与待核对事项
- 说明开发团队应使用 dist/ 中编译后的文件,作者在 src/ 中工作,属于工作流约定而非许可限制。
- 若使用 Google Fonts 需自行在 base.njk 的 head 中添加 link,属于外部服务集成提示。
- 部署到 Netlify、Vercel 等托管平台可能产生外部服务费用,需自行评估。
- 演示图片、字体、图标及其他第三方素材需按各自许可核对,不能仅凭代码的 MIT 许可判断。
- 域名、托管、数据库、支付或 AI 等外部服务可能另外收费。
查看原文依据
Permission is hereby granted, free of charge
许可证原文
El equipo de desarrollo consume los archivos compilados en `dist/`. El autor trabaja en `src/`.
README 原文
Si usas Google Fonts, añadir el `<link>` en `src/_includes/layouts/base.njk` dentro del `<head>`.
README 原文
https://github.com/Osvalam86/eleventy-starter/blob/main/LICENSEhttps://github.com/Osvalam86/eleventy-starter/blob/main/README.mdPlataformas compatibles: **Netlify**, **Vercel**, **GitHub Pages**, **Cloudflare Pages** y cualquier servidor estático (Apache, nginx).
README 原文
DeepSeek 根据仓库许可证和 README 整理 ·2026-10-02。结果仅覆盖已读取资料,依赖、图片、字体及后续条款变更以各自原文为准。
项目核对记录与介绍依据
模板资料
- 来源平台
- GitHub
- 资料与维护参考分
- 资料较完整 · 83 / 100
- 技术栈
- Eleventy
- 网站类型
- 启动器
这个分数如何计算
本站按许可资料(最高30分)、演示链接(12分)、预览图(10分)、Star数(15分)、记录的维护时间(15分)、描述完整度(8分)、功能记录(5分)和分类资料(5分)计算。它不是代码、安全或性能测试,也不是 Google 评分;有链接不代表已实测可用。
这个项目需要核对什么
- 记录的仓库更新时间
- 2026-04-28
- 截图检查日期
- 2026-10-02
已收录演示链接。请用你的内容与屏幕尺寸检查;有演示不代表表单、认证或支付已经可用。
目录记录的主要技术栈是 Eleventy。请通过依赖清单和锁文件核对实际版本与托管要求。这些说明基于收录资料,不代表代码审计或部署实测。
查看具体选型方法 →介绍的制作与核对状态
介绍由 AI 辅助整理目录与仓库资料。仓库作者不等于这篇介绍的审核者。
已完成服务器许可规则与 AI 来源核对;未经过人工编辑审核。
查看当时使用的来源资料
SHA-256: bb892adad206e8118468e6effcb05ed2ad45b90c46d884d5d727327f80a945a0
2026-10-02T11:12:53.697Z
来源文件 / 仓库# Eleventy Starter
Starter template con Eleventy + Tailwind CSS v4 listo para proyectos rápidos.
## 🚀 Demo
https://eleventy-starter-osva.netlify.app/
## Preview

## ✨ Features
- Eleventy v3 + Nunjucks
- Tailwind CSS v4 utility-first
- Sistema de tokens CSS (primitivos + semánticos)
- Componentes accesibles (JS vanilla)
- Ready para deploy (Netlify, Vercel, etc.)








Starter template para proyectos HTML estáticos. Nunjucks como motor de templates, Tailwind CSS v4 con arquitectura modular de tokens y JS vanilla para componentes accesibles.
El equipo de desarrollo consume los archivos compilados en `dist/`. El autor trabaja en `src/`.
---
## Inicio rápido
Clona el template y empieza a trabajar en segundos:
```bash
# Clonar el repositorio
git clone https://github.com/Osvalam86/eleventy-starter.git mi-proyecto
cd mi-proyecto
# Instalar dependencias
pnpm install
# Iniciar servidor de desarrollo
pnpm run dev
```
Abrir [http://localhost:8080](http://localhost:8080).
---
## Scripts
| Comando | Descripción |
| ------------------- | --------------------------------------------------------- |
| `pnpm run dev` | Servidor de desarrollo con hot-reload en `localhost:8080` |
| `pnpm run build` | Genera el sitio estático en `dist/` (CSS minificado) |
| `pnpm run clean` | Elimina la carpeta `dist/` |
| `pnpm format` | Formatea todos los archivos `src/` con Prettier |
| `pnpm format:check` | Verifica formato sin modificar archivos (útil en CI) |
El script `dev` corre Eleventy y Tailwind CLI en paralelo. El script `build` los ejecuta en secuencia: CSS primero, luego HTML.
---
## Estructura de carpetas
```
eleventy-starter/
├── .eleventy.js # Configuración de Eleventy
├── .eleventyignore # Archivos que Eleventy debe ignorar
├── .prettierignore
├── prettier.config.js # Prettier + prettier-plugin-tailwindcss
├── .vscode/
│ └── settings.json # formatOnSave automático con Prettier
├── package.json
│
├── src/
│ ├── _data/
│ │ ├── site.json # Metadata global (nombre, url, idioma, copyright)
│ │ └── nav.json # Items de navegación
│ │
│ ├── _includes/
│ │ ├── layouts/
│ │ │ ├── base.njk # Shell HTML completo (head, body, scripts)
│ │ │ └── page.njk # Layout estándar (extiende base.njk)
│ │ ├── partials/
│ │ │ ├── header.njk # Barra de navegación
│ │ │ └── footer.njk # Pie de página
│ │ └── components/
│ │ ├── button.njk # Macro: Button(props)
│ │ ├── card.njk # Macro: Card(props)
│ │ └── dialog.njk # Macro: Dialog(props) con {% call %}
│ │
│ ├── assets/
│ │ ├── css/
│ │ │ ├── input.css # Entry point de Tailwind CSS
│ │ │ ├── primitive.css # Tokens crudos en :root
│ │ │ ├── theme.css # Tokens semánticos en @theme
│ │ │ ├── base.css # Estilos base del body y tipografía (CSS puro)
│ │ │ └── utilities/
│ │ │ ├── focus.css
│ │ │ └── scrollbar.css
│ │ ├── images/ # Imágenes (copiadas tal cual a dist/)
│ │ └── fonts/ # Fuentes (copiadas tal cual a dist/)
│ │
│ ├── js/
│ │ ├── main.js # Entry point — importa e inicializa componentes
│ │ └── components/
│ │ ├── dialog.js
│ │ ├── dropdown.js
│ │ ├── menu.js
│ │ └── tabs.js
│ │
│ └── pages/
│ ├── index.njk → dist/index.html
│ ├── index.11tydata.json # Data de la home (layout, título, contenido)
│ ├── about/
│ │ ├── index.njk → dist/about/index.html
│ │ └── index.11tydata.json
│ └── components/
│ ├── index.njk → dist/components/index.html
│ └── index.11tydata.json
│
└── dist/ # BUILD OUTPUT — no incluido en git
├── index.html
├── css/styles.css
└── js/main.js
```
---
## Arquitectura CSS
El sistema de tokens usa dos capas separadas para evitar acoplamiento entre valores crudos y su uso semántico.
### Capa 1 — `primitive.css`
Define los valores crudos en `:root`. No genera utilidades de Tailwind. Sirve como fuente única de verdad para colores, escalas y valores base del proyecto.
```css
/* src/assets/css/primitive.css */
:root {
--color-primary-50: #eef2ff;
--color-primary-100: #e0e7ff;
}
```
### Capa 2 — `theme.css`
Define los tokens semánticos dentro de `@theme`. Tailwind v4 lee este bloque y genera automáticamente todas las utilidades: `text-brand-500`, `bg-brand-100`, `border-brand-700`, etc.
```css
/* src/assets/css/theme.css */
@theme {
/* Colores de marca */
--color-brand-50: var(--color-primary-50);
--color-brand-100: var(--color-primary-100);
--color-brand-900: var(--color-primary-900);
/* Tipografía */
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, monospace;
}
```
**Para adaptar el starter a un nuevo proyecto:** modifica los valores en `primitive.css` y actualiza las referencias en `theme.css`. El resto del código no cambia.
### Estilos en componentes
Los estilos viven directamente en cada macro `.njk`, sin archivos CSS separados ni `@apply`. Las variantes se resuelven con mapas Nunjucks que devuelven las clases Tailwind correspondientes:
```njk
{# button.njk #}
{%- set sizeMap = {
"sm": "px-3 py-1.5 text-xs",
"md": "px-4 py-2 text-sm",
"lg": "px-6 py-3 text-base"
} -%}
{%- set variantMap = {
"primary": "bg-brand-500 text-white hover:bg-brand-600",
"secondary": "border border-gray-300 bg-white text-gray-700 hover:bg-gray-50",
"ghost": "text-gray-600 hover:bg-gray-100 hover:text-gray-900"
} -%}
```
Para los elementos que no usan macro (badges inline, campos de formulario), las clases se escriben directamente en el HTML del template que los consume.
---
## Crear páginas
Cada página vive en su propia subcarpeta dentro de `src/pages/` y se compone de dos archivos:
- `index.njk` — el template HTML/Nunjucks, sin front matter
- `index.11tydata.json` — el data file con metadata y contenido de la página
```
src/pages/
└── mi-pagina/
├── index.njk
└── index.11tydata.json → aplica solo a index.njk de esta carpeta
```
**`index.11tydata.json`** — controla el layout, la ruta y el contenido:
```json
{
"layout": "page.njk",
"permalink": "/mi-pagina/index.html",
"title": "Mi página",
"pageTitle": "Título visible en el H1",
"pageSubtitle": "Subtítulo opcional debajo del H1.",
"sections": [{ "heading": "Sección uno", "body": "Texto de la sección." }]
}
```
**`index.njk`** — solo markup, consume las variables del data file:
```njk
<div class="space-y-10">
{% for section in sections %}
<section>
<h2 class="text-base font-semibold text-gray-900">{{ section.heading }}</h2>
<p class="text-sm text-gray-500">{{ section.body }}</p>
</section>
{% endfor %}
</div>
```
> Este patrón usa el [Data Cascade de Eleventy](https://www.11ty.dev/docs/data-cascade/): el `.11tydata.json` aplica únicamente al template con el mismo nombre, sin contaminar el namespace global de `_data/`.
---
## Macros de componentes
Importa el macro al inicio de la página y llámalo con un objeto de props.
### Button
Genera `<button>` o `<a>` según si se pasa `href`.
```njk
{% from "components/button.njk" import Button %}
{# Botón estándar #}
{{ Button({ label: "Guardar", variant: "primary" }) }}
{# Enlace con tamaño grande #}
{{ Button({ label: "Ver más", variant: "secondary", size: "lg", href: "/about/" }) }}
{# Con atributos HTML adicionales #}
{{ Button({ label: "Abrir", variant: "ghost", attrs: 'data-component="dialog-trigger" data-target="mi-dialog"' }) }}
```
| Prop | Tipo | Default | Opciones |
| ---------- | ------- | ----------- | ----------------------------------------- |
| `label` | string | — | Texto del botón (requerido) |
| `variant` | string | `"primary"` | `"primary"` \| `"secondary"` \| `"ghost"` |
| `size` | string | `"md"` | `"sm"` \| `"md"` \| `"lg"` |
| `href` | string | — | Convierte el elemento en `<a>` |
| `type` | string | `"button"` | `"button"` \| `"submit"` \| `"reset"` |
| `disabled` | boolean | — | Deshabilita el elemento |
| `attrs` | string | — | Atributos HTML adicionales como string |
### Card
Tarjeta de contenido con badge opcional y variante de enlace.
```njk
{% from "components/card.njk" import Card %}
{# Tarjeta estática #}
{{ Card({ title: "Título", description: "Descripción de la tarjeta." }) }}
{# Con badge #}
{{ Card({ title: "Artículo", description: "Texto.", badge: "Nuevo" }) }}
{# Toda la tarjeta como enlace #}
{{ Card({ title: "Ver más", description: "Texto.", href: "/articulo/", badge: "Link" }) }}
{# Sin efecto hover #}
{{ Card({ title: "Estático", description: "Sin hover.", hover: false }) }}
```
| Prop | Tipo | Default | Descripción |
| ------------- | ------- | ------- | ------------------------------------------- |
| `title` | string | — | Título de la tarjeta (requerido) |
| `description` | string | — | Texto descriptivo |
| `badge` | string | — | Badge encima del título |
| `href` | string | — | Convierte la tarjeta en enlace |
| `hover` | boolean | `true` | Agrega `hover:shadow-md` al pasar el cursor |
| `class` | string | — | Clases CSS adicionales |
### Dialog
Usa `{% call %}` para pasar el contenido del cuerpo al macro.
```njk
{% from "components/button.njk" import Button %}
{% from "components/dialog.njk" import Dialog %}
{# Trigger — puede estar en cualquier parte de la página #}
{{ Button({
label: "Abrir",
variant: "secondary",
attrs: 'data-component="dialog-trigger" data-target="mi-dialog"'
}) }}
{# Panel del dialog #}
{% call Dialog({ id: "mi-dialog", title: "Confirmar acción", size: "sm" }) %}
<p class="text-sm text-gray-600 mb-6">Esta acción no se puede deshacer.</p>
<div class="flex justify-end gap-3">
{{ Button({ label: "Cancelar", variant: "ghost", attrs: 'data-component="dialog-close"' }) }}
{{ Button({ label: "Confirmar", variant: "primary" }) }}
</div>
{% endcall %}
```
| Prop | Tipo | Default | Descripción |
| ------------- | ------ | ------- | ----------------------------------------------------------------------------- |
| `id` | string | — | ID único del dialog (requerido, debe coincidir con `data-target` del trigger) |
| `title` | string | — | Título en el header |
| `description` | string | — | Subtítulo opcional en el header |
| `size` | string | `"md"` | `"sm"` \| `"md"` \| `"lg"` \| `"xl"` |
| `class` | string | — | Clases adicionales al panel |
El dialog gestiona foco, `aria-modal`, cierre con `Escape` y click en backdrop de forma automática vía `dialog.js`.
---
## Agregar componentes JS
Cada componente exporta una función `init(root)`. El atributo `data-component` conecta el comportamiento con el HTML sin acoplarse a clases CSS.
**1. Crear el módulo en `src/js/components/`:**
```javascript
// src/js/components/tooltip.js
export function initTooltips(root) {
root.querySelectorAll('[data-component="tooltip"]').forEach((el) => {
// lógica del componente
});
}
```
**2. Importar e inicializar en `src/js/main.js`:**
```javascript
import { initTooltips } from "./components/tooltip.js";
document.addEventListener("DOMContentLoaded", () => {
initTooltips(document);
});
```
**3. Usar en el template con `data-component`:**
```html
<span data-component="tooltip" data-tip="Texto del tooltip"> Hover aquí </span>
```
---
## Adaptar para un nuevo proyecto
Al usar este starter como base, sigue este checklist antes de empezar a maquetar.
### 1. Renombrar el repositorio y el paquete
Si clonas el repo manualmente (sin usar "Use this template" en GitHub):
```bash
# Clonar con el nombre del nuevo proyecto
git clone https://github.com/Osvalam86/eleventy-starter.git mi-nuevo-proyecto
cd mi-nuevo-proyecto
# Desconectar del repositorio de origen y apuntar al nuevo remote
git remote remove origin
git remote add origin https://github.com/TU_USUARIO/mi-nuevo-proyecto.git
```
Actualizar el nombre del paquete en `package.json`:
```json
{
"name": "mi-nuevo-proyecto"
}
```
### 2. Actualizar los datos del sitio
Editar `src/_data/site.json` con los datos reales del proyecto:
```json
{
"name": "Nombre del Proyecto",
"description": "Descripción del proyecto.",
"url": "https://mi-proyecto.com",
"lang": "es",
"copyright": "Todos los derechos reservados."
}
```
Editar `src/_data/nav.json` con la navegación real. Elimina o agrega items según la estructura de páginas del proyecto:
```json
{
"primary": [
{ "label": "Inicio", "url": "/" },
{ "label": "Servicios", "url": "/servicios/" }
],
"footer": [{ "label": "Privacidad", "url": "/privacidad/" }]
}
```
### 3. Reemplazar la paleta de colores
Abrir `src/assets/css/primitive.css` y sustituir los valores hex de `--color-primary-*` con la paleta del proyecto. Figma exporta colores en hex directamente — pégalos aquí:
```css
:root {
--color-primary-50: #fdf4ff; /* ← reemplazar con la paleta del proyecto */
--color-primary-100: #fae8ff;
--color-primary-200: #f5d0fe;
--color-primary-300: #f0abfc;
--color-primary-400: #e879f9;
--color-primary-500: #d946ef; /* ← color principal */
--color-primary-600: #c026d3;
--color-primary-700: #a21caf;
--color-primary-800: #86198f;
--color-primary-900: #701a75;
}
```
`theme.css` no necesita cambios — las referencias `var(--color-primary-*)` apuntan automáticamente a los nuevos valores. Las utilidades de Tailwind (`text-brand-500`, `bg-brand-100`, etc.) se regeneran solas.
### 4. Actualizar las fuentes
En `src/assets/css/theme.css`, cambiar las fuentes del proyecto:
```css
@theme {
--font-sans: "Tu Fuente", ui-sans-serif, system-ui, sans-serif;
--font-mono: ui-monospace, monospace;
}
```
Si usas Google Fonts, añadir el `<link>` en `src/_includes/layouts/base.njk` dentro del `<head>`.
### 5. Limpiar las páginas de demo
Eliminar o vaciar el contenido de las páginas de ejemplo. Cada página se compone de su `index.njk` y su `index.11tydata.json`:
- `src/pages/index.*` — reemplazar con la home real
- `src/pages/about/` — adaptar o eliminar la carpeta completa
- `src/pages/components/` — mantener como referencia interna o eliminar si no es necesario
### Lo que NO hay que cambiar
- La estructura de carpetas `src/` — Eleventy y Tailwind dependen de ella
- Los archivos en `src/_includes/` — los macros y layouts son reutilizables tal cual
- Los archivos en `src/js/` — los componentes JS funcionan sin configuración adicional
- `src/assets/css/theme.css` — solo cambiar si se necesitan tokens semánticos nuevos
---
## Prettier
El proyecto incluye [Prettier](https://prettier.io/) con [`prettier-plugin-tailwindcss`](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) para ordenar clases Tailwind automáticamente.
La configuración está en `prettier.config.js`. El plugin necesita saber dónde está el CSS de entrada de Tailwind v4 (no hay `tailwind.config.js`), por eso se declara `tailwindStylesheet`.
**Uso en el día a día:**
Si usas VSCode, el formato se aplica automáticamente al guardar (configurado en `.vscode/settings.json`). Para formatear todo el proyecto manualmente:
```bash
# Formatea y reescribe
pnpm format
# Solo verifica sin modificar (útil en CI)
pnpm format:check
```
> `src/_includes/layouts/` está excluido del formateo en `.prettierignore` porque los layouts usan comentarios Nunjucks `{# #}` que el parser HTML de Prettier no soporta. El resto de `.njk`, `.js` y `.css` se formatea sin problemas.
---
## Deploy
El output en `dist/` son archivos HTML, CSS y JS estáticos — se pueden servir desde cualquier hosting estático.
```bash
# Generar build de producción
pnpm run build
```
El contenido de `dist/` está listo para subir. Plataformas compatibles: **Netlify**, **Vercel**, **GitHub Pages**, **Cloudflare Pages** y cualquier servidor estático (Apache, nginx).
### Demo en producción: https://eleventy-starter-osva.netlify.app/
---
## Licencia
MIT © Osvaldo Ocampo
---
## Autor
[](https://github.com/Osvalam86)
[](https://medium.com/@osvaldo.ocampo)
看看同类模板
对比同技术栈与用途的模板,找到更适合的视觉与内容结构。

eleventy-base-blog
A starter repository for a blog web site using the Eleventy static site generator.
Eleventy · 博客

eleventy-garden
:seedling: A starter site for building a mind garden with eleventy
Eleventy · 启动器

eleventastic
An Eleventy Starter Kit
Eleventy · 启动器

neat-starter
Starter Template for Netlify CMS, Eleventy, Alpine JS & Tailwind CSS
Eleventy · 启动器

Eleventy-Starter-Boilerplate
🚀 Eleventy Starter is production-ready with SEO-friendly for quickly starting a blog. ⚡ Built with Eleventy, …
Eleventy · 博客

eleventy-plus-vite
A clean and fast Eleventy Starter Project with Vite
Eleventy · 启动器
