An open API service indexing awesome lists of open source software.

https://github.com/gdewalters/11ty-project-base

Opinionated starter for my projects—Eleventy + Tailwind, dark mode, prose, and Netlify config.
https://github.com/gdewalters/11ty-project-base

11ty 11ty-starter 11ty-template github-actions netlify netlify-deployment tailwind tailwindcss-v4

Last synced: about 1 month ago
JSON representation

Opinionated starter for my projects—Eleventy + Tailwind, dark mode, prose, and Netlify config.

Awesome Lists containing this project

README

          

# 11ty project base

A clean, future‑proof Eleventy v3 starter wired with Tailwind CSS v4, a tasteful prose theme, dark mode, smart minification, first‑class 404/500 pages, optional SPA fallback, and a one‑command dev workflow. Minimal ceremony, maximum mileage.

---

## Features

- **Eleventy v3 (ESM)** with a tidy directory layout
- **Tailwind v4 (CLI)** with tokens via `@theme`, Typography plugin, and a tiny base prose theme
- **Dark mode** toggle (no FOUC, respects OS preference)
- **Minification in production** (`html-minifier-terser`), XML/RSS kept safe
- **404 & 500 pages** at the site root; SEO‑safe `noindex`
- **Optional SPA fallback** via `_redirects` (or `vercel.json`)
- **JS bundling** via esbuild (context API), outputs to `public/assets/js`
- **Netlify‑ready** (`netlify.toml` with headers + Node version)
- **One command to rule them all**: `npm run dev:all`

---

## Using this starter for a new project

Spin up a fresh site from this starter in a brand-new directory. Pick the route that suits you:

### Option A — GitHub “Use this template” (UI)
1. Open this repo on GitHub and click **Use this template → Create a new repository**.
2. Name your new repo (e.g. `my-new-site`) and create it.
3. Clone it locally:
```bash
git clone git@github.com:/my-new-site.git
cd my-new-site
npm install
npm run dev:all
```

### Option B — GitHub CLI (from template)
Replace `OWNER/REPO` with this starter’s full name.
```bash
gh repo create my-new-site --template OWNER/REPO --public --clone
cd my-new-site
npm install
npm run dev:all
```

### Option C — Shallow clone (no template UI)
```bash
git clone --depth=1 git@github.com:OWNER/REPO.git my-new-site
cd my-new-site
rm -rf .git
git init -b main
git add .
git commit -m "chore: bootstrap from starter"
# (optional) set up the remote:
# git remote add origin git@github.com:/my-new-site.git
# git push -u origin main
npm install
npm run dev:all
```

---

### Post-setup checklist (2 minutes)

- **Project metadata**
- `package.json`: update `name`, `description`, `repository`, `author`, `homepage`, `bugs`.
- `README.md`: change the short description to match the new site.
- `LICENCE`: ensure the copyright holder/year suit this project.

- **Site identity**
- `_data/site.js`: set your site `name` (and any other fields you add later).
- `_includes/layouts/base.njk`: update header nav links (and email in 404/500).
- Replace `public/assets/images/example.jpg` with a real image or remove it.

- **Dev & build**
- Ensure Node **v20+** (run `nvm use` if you use `nvm`; see `.nvmrc`).
- Dev: `npm run dev:all` → http://localhost:8080
- Prod build: `npm run build:all` (outputs to `_site/`)

- **Deploy (Netlify)**
- Connect the new repo in Netlify.
- Build command: `npm run build:all`
- Publish directory: `_site`
- Node version: 20 (in **Site settings** or via `netlify.toml`).
- `_redirects` and headers in `netlify.toml` are picked up automatically.

- **CI (GitHub Actions)**
- The included workflow builds on pushes/PRs to `main`/`master`.
- Check the **Actions** tab after your first push.

---

### Common tweaks
- Want a different container width? Adjust `.container-page` (and optionally add `.container-narrow`) in `styles/Tailwind.css`.
- Need extra colours or typography? Add tokens under `@theme` and style `.prose` as needed.
- Not using JS yet? Remove the `JS` process from `dev:all` or leave it—it’s harmless.

---

## Directory structure

```
11ty-project-base/
├─ _config/ # Eleventy hooks
│ ├─ filters.js
│ ├─ shortcodes.js
│ └─ transforms.js
├─ _data/ # Global data
│ ├─ env.js
│ └─ site.js
├─ _helpers/
│ └─ build-js.js # esbuild bundler (context API)
├─ _includes/
│ └─ layouts/
│ | └─ base.njk
│ └─ partials/
├─ content/ # Eleventy input
│ ├─ 404.njk
│ ├─ 500.njk
│ ├─ index.njk
│ └─ styles/
│ └─ main.css # generated by Tailwind CLI (gitignored)
├─ public/ # passthrough to site root
│ ├─ _redirects # optional
│ └─ assets/
│ └─ images/
│ └─ example.jpg
├─ scripts/ # your app JS entry(ies)
│ └─ main.js
├─ styles/
│ └─ Tailwind.css # Tailwind v4 entry (source)
├─ _site/ # Eleventy output (gitignored)
├─ .gitignore
├─ .nvmrc
├─ eleventy.config.js
├─ netlify.toml
└─ package.json
```

---

## Requirements

- Node **v20+** (see `.nvmrc`)
- npm

---

## Quick start

```bash
npm install
npm run dev:all
# open http://localhost:8080
```

**Production build:**
```bash
npm run build:all
```

---

## NPM scripts

```json
{
"scripts": {
"dev": "ELEVENTY_ENV=dev eleventy --serve",
"build": "ELEVENTY_ENV=prod eleventy",
"clean": "rimraf _site",

"tw:init": "tailwindcss -i styles/Tailwind.css -o content/styles/main.css",
"tw:watch": "tailwindcss -i styles/Tailwind.css -o content/styles/main.css --watch",
"tw:build": "tailwindcss -i styles/Tailwind.css -o content/styles/main.css --minify",

"js:watch": "node _helpers/build-js.js --watch",
"js:build": "node _helpers/build-js.js",

"predev:all": "npm run tw:init",
"dev:all": "concurrently -k -n ELEVENTY,TAILWIND,JS -c auto \"npm:dev\" \"npm:tw:watch\" \"npm:js:watch\"",
"build:all": "npm run tw:build && npm run js:build && npm run build"
}
}
```

- `dev:all` runs Eleventy + Tailwind watcher + esbuild watcher.
- `build:all` compiles Tailwind, bundles JS, then builds Eleventy (with minification).

---

## Eleventy configuration

`eleventy.config.js`:
- **Input**: `content/`
- **Includes**: `_includes/`
- **Data**: `_data/`
- **Output**: `_site/`
- **Passthrough**: `public/` → `/`, `content/styles/` → `/assets` (so compiled CSS is served as `/assets/main.css`)
- **Watches**: `content/styles`, `styles`, `scripts`
- **Transforms**: registered from `_config/transforms.js`
- **Template engines**: Nunjucks for html/md/data

---

## Minification and XML safety

`_config/transforms.js` includes two transforms:

1. **`html-minify`** (prod only): uses `html-minifier-terser` for `.html?` files with safe defaults (`collapseWhitespace`, `removeComments`, `minifyJS/CSS`, etc.).
2. **`xml-preserve`**: for `.xml`, `.rss`, `.atom`, `.xsl`, we **do not minify**; we only **normalise** newlines and trim trailing whitespace to keep feeds/sitemaps parser‑friendly.

Production mode is toggled by `ELEVENTY_ENV=prod` in the `build` script.

---

## Tailwind v4 setup

- Entry CSS: `styles/Tailwind.css`
- Output CSS: `content/styles/main.css` (gitignored; copied to `/assets/main.css`)
- Plugins: `@tailwindcss/typography`
- Tokens: declared via `@theme` (e.g., `--color-vividcrimson`, `--color-huntergreen`)
- Sources: `@import "tailwindcss" source("../content");` and `@source "../_includes";` to scan Eleventy templates.

### Base prose theme and links

- All links: subtle underline, medium weight, **crimson**; underline removed on hover/focus.
- `.prose` content: neutral text, **muted hunter‑green** blockquotes (text + left border), sensible code/pre colours.

Apply to Markdown/CMS output by wrapping rendered HTML with `.prose` (e.g., on article containers or template partials).

---

## Dark mode (no FOUC + toggle)

- I set `` early via a tiny inline script in `base.njk` (uses `localStorage` or OS preference).
- Dark tokens adjust link colour (brighter crimson), quote accents, prose neutrals, and card backgrounds.
- A **“Toggle theme”** button in the header flips state and saves preference.

You can move the toggle script into `scripts/main.js` later if you prefer bundling.

---

## Header/footer layout

- Header/footer content is wrapped with `.container-page` (or `.container-narrow` if enabled) to align with main content.
- Optional subtle backgrounds with dark‑mode variants to separate header/footer from main content.

---

## 404 & 500 pages

- `content/404.njk` → `/404.html`
- `content/500.njk` → `/500.html`
- Both use `noindex` and are excluded from collections.
- Eleventy Dev Server routes unknown paths to `/404.html` locally.
- Netlify/Vercel/Cloudflare Pages/GitHub Pages use `/404.html` automatically for static sites.

---

## Redirects & SPA fallback

- `public/_redirects` is passed through to the site root:
- Add custom redirects like `/old /new 301`.
- **Optional SPA**: uncomment `/* /index.html 200` to route all paths to the homepage (disables static 404).
- **Vercel**: use `vercel.json` rewrites if you need SPA behaviour.
- **GitHub Pages**: no rewrites; use the “404‑as‑SPA loader” trick if you must ship an SPA.

> Do not enable SPA fallback **and** keep a static 404 at the same time. Pick one.

---

## JavaScript bundling

- Entry: `scripts/main.js`
- Bundler: `_helpers/build-js.js` uses **esbuild context API** for watching:
- Output: `public/assets/js/bundle.js` (passthrough to `_site/assets/js`)
- Include in layout: ``

---

## Netlify deploy

`netlify.toml`:

```toml
[build]
command = "npm run build:all"
publish = "_site"

[build.environment]
NODE_VERSION = "20"

[[headers]]
for = "/assets/*"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"

[[headers]]
for = "/*"
[headers.values]
Referrer-Policy = "strict-origin-when-cross-origin"
X-Content-Type-Options = "nosniff"
X-Frame-Options = "DENY"
```

- Netlify will also read `_redirects` from the published root.
- If you convert to an SPA later, add a 200 rewrite here **or** in `_redirects` (not both).

---

## Global data

- `_data/site.js`: name/year for layout chrome.
- `_data/env.js`: simple environment surface (`ELEVENTY_ENV`, Node version) for templates/data.

---

## Customising

- Add collections (e.g., `content/posts/`) and define them in `eleventy.config.js`.
- Extend typography (e.g., `.prose-lg` sections) with component classes.
- Add more filters/shortcodes in `_config/filters.js` and `_config/shortcodes.js`.
- Replace the grid tiles on the homepage with your real sections.

---

## Troubleshooting

- **Passthrough collision**:
Error: “Multiple passthrough copy files are trying to write to the same output file (`_site/assets/main.css`).”
Fix: only passthrough **compiled** CSS (`content/styles` → `/assets`). Do **not** passthrough `styles/`.

- **esbuild “Invalid option 'watch'”**:
Use the **context API** (`const ctx = await esbuild.context(opts); await ctx.watch();`) as implemented in `_helpers/build-js.js`.

- **No CSS on first load**:
Ensure `predev:all` runs `tw:init` so `content/styles/main.css` exists before Eleventy starts.

- **SPA fallback eats 404**:
If you enable `/* /index.html 200`, you will not see your static 404. That’s expected.

- **Dark mode flashes**:
Make sure the inline theme bootstrap runs **before** the CSS link in `base.njk`.

---

## Licence

See [`LICENCE`](LICENCE).

---

## Credits

Built with Eleventy and Tailwind. Happy building!