717 lines
No EOL
27 KiB
Markdown
717 lines
No EOL
27 KiB
Markdown
# Hypnagaga - Eleventy Project
|
||
|
||
This is an Eleventy (11ty) static site generator project, based on [eleventy-excellent](https://github.com/madrilene/eleventy-excellent.git), customized for hosting music mixes with track listings and annotations.
|
||
|
||
**Tech Stack**: Eleventy 3.x (ESM), Tailwind CSS (token-based), CUBE CSS methodology, WebC components, Hotwired Turbo for navigation, PostCSS, esbuild
|
||
|
||
## Quick Start
|
||
|
||
```bash
|
||
npm install # Install dependencies
|
||
npm start # Dev server at localhost:8080
|
||
npm run build # Production build
|
||
npm run colors # Regenerate color tokens
|
||
npm run test:a11y # Run accessibility tests
|
||
```
|
||
|
||
**Key Files to Know:**
|
||
- `eleventy.config.js` - Main Eleventy config (imports from `src/_config/`)
|
||
- `src/_config/` - Modular config (collections, filters, events, plugins, shortcodes)
|
||
- `src/_data/designTokens/` - Design tokens (colors, spacing, fonts)
|
||
- `src/pages/projects/mixes/` - Music mix tracks (markdown files)
|
||
- `src/assets/css/global/` - Global CSS (CUBE methodology)
|
||
|
||
## Architecture & Key Concepts
|
||
|
||
### Music Mix System
|
||
The project's core feature is a hierarchical music mix/track system:
|
||
- **Projects** (e.g., "TomorrowsBacon") contain multiple tracks
|
||
- **Tracks** are individual markdown files in `src/pages/projects/mixes/{project-name}/`
|
||
- Tracks have frontmatter: `project`, `track_number`, `artist`, `album`, `albumArt`, `start_time`
|
||
- Collection `tracksByProject` (defined in `src/_config/collections.js`) groups and sorts tracks by project
|
||
- Layouts: `mix.njk` shows track list, `mix-track.njk` shows individual track with prev/next navigation
|
||
- Turbo Drive provides SPA-like navigation between tracks without full page reloads
|
||
|
||
### Configuration Architecture
|
||
**ESM-based modular config** in `src/_config/` - all files use `export` syntax:
|
||
- `collections.js` - Eleventy collections (getAllPosts, tagList, tracksByProject)
|
||
- `events.js` - Build hooks (CSS/JS compilation, pagefind indexing)
|
||
- `filters.js` - Template filters (dates, markdown, slugify)
|
||
- `plugins.js` - Eleventy plugins (image transform, WebC, RSS, syntax highlighting)
|
||
- `shortcodes.js` - Reusable template functions (image, svg, animateText)
|
||
|
||
Each category imports from subdirectories (e.g., `filters/dates.js`) and exports a consolidated object.
|
||
|
||
### Design Tokens & Tailwind
|
||
- Design tokens stored as JSON in `src/_data/designTokens/` (colors, spacing, fonts, viewports)
|
||
- `tokensToTailwind()` and `clampGenerator()` utilities convert tokens to Tailwind config
|
||
- Tailwind is customized to generate utilities from design tokens (e.g., `mt-xs-s`, `bg-pink`)
|
||
- Custom properties generated via Tailwind plugin (e.g., `--color-pink`, `--space-m`)
|
||
- **Run `npm run colors`** after editing `colorsBase.json` to regenerate `colors.json` with shades
|
||
- Tailwind's preflight/reset is **disabled** - custom reset in `src/assets/css/global/reset.css`
|
||
|
||
### CSS Architecture (CUBE CSS)
|
||
Layer order in `src/assets/css/global/global.css`:
|
||
1. `tailwindcss/base` (mostly unused, preflight disabled)
|
||
2. Custom reset, fonts
|
||
3. `tailwindcss/components` (where custom properties are injected)
|
||
4. Variables, global styles
|
||
5. Blocks, compositions, utilities (via `@import-glob`)
|
||
6. `tailwindcss/utilities`
|
||
|
||
**Key patterns:**
|
||
- Use compositions (grid, flow, wrapper) over Tailwind's container
|
||
- Blocks for component-specific styles
|
||
- Utilities for single-purpose helpers (e.g., text animations)
|
||
- Custom utilities like `flow-space-*`, `gutter-*`, `region-space-*` control layout spacing
|
||
|
||
### Build Process
|
||
**Before Eleventy runs** (`eleventy.before` event):
|
||
1. `buildAllCss()` processes CSS with PostCSS (import-glob → tailwind → autoprefixer → cssnano)
|
||
- Global CSS: `src/assets/css/global/global.css` → `src/_includes/css/global.css` (inlined)
|
||
- Local CSS: `src/assets/css/local/**/*.css` → `src/_includes/css/` (per-page bundles)
|
||
- Component CSS: `src/assets/css/components/**/*.css` → `dist/assets/css/components/`
|
||
2. `buildAllJs()` bundles JS with esbuild
|
||
- Inline bundle: `src/assets/scripts/bundle/**/*.js` → `src/_includes/scripts/`
|
||
- Defer bundle: similar process
|
||
- Component scripts: `src/assets/scripts/components/**/*.js` → `dist/assets/components/`
|
||
|
||
**After Eleventy builds** (`eleventy.after` event):
|
||
- `buildPagefind()` generates search index (only in production)
|
||
|
||
**Watch targets**: `src/assets/**/*.{css,js,svg,png,jpeg}`, `src/_includes/**/*.webc`
|
||
|
||
### Image Handling
|
||
Three methods for image optimization (via @11ty/eleventy-img):
|
||
1. **HTML Transform**: Automatically processes `<img>` elements in HTML output
|
||
2. **Markdown**: `` → transformed to `<picture>` element
|
||
3. **Shortcodes**: `{% image '/path/to/image.jpg', 'alt text' %}` or `{% imageKeys {...} %}`
|
||
|
||
Images are processed to WebP/JPEG, multiple widths, and optimized. Source paths are prepended with `./src` automatically in shortcodes.
|
||
|
||
## Development Workflows
|
||
|
||
### Essential Commands
|
||
- **`npm start`** - Dev server with live reload (runs `dev:11ty`)
|
||
- **`npm run build`** - Full production build (clean → build:11ty → build:search)
|
||
- **`npm run colors`** - Regenerate color palettes from `colorsBase.json`
|
||
- **`npm run favicons`** - Generate favicons from `src/assets/svg/misc/logo.svg`
|
||
- **`npm run test:a11y`** - Run Pa11y accessibility tests
|
||
|
||
### Adding a New Track
|
||
1. Create markdown file in `src/pages/projects/mixes/{project-name}/{track-number}-{slug}.md`
|
||
2. Include frontmatter:
|
||
```yaml
|
||
---
|
||
title: "Song Title"
|
||
artist: "Artist Name"
|
||
album: "Album Name"
|
||
project: ProjectName # must match existing project
|
||
track_number: 1
|
||
albumArt: "/pages/projects/mixes/{project}/image.jpg"
|
||
---
|
||
```
|
||
3. Track automatically appears in `collections.tracksByProject[project]`
|
||
4. Navigation (prev/next) auto-generates in `mix-track.njk` layout
|
||
|
||
### Working with Design Tokens
|
||
1. Edit JSON files in `src/_data/designTokens/`
|
||
2. For colors: Edit `colorsBase.json` → run `npm run colors` → regenerate `colors.json`
|
||
3. Tokens flow to: CSS custom properties, Tailwind utilities, and templates (via `{{ designTokens }}`)
|
||
4. Fluid type/space uses Utopia-style clamps (configured in `clamp-generator.js`)
|
||
|
||
### Text Animations
|
||
Use the `animateText` shortcode or markdown-it-attrs classes:
|
||
```jinja2
|
||
{% animateText "Hello", "shiver", "1.5" %} {# content, animation, speed #}
|
||
```
|
||
Available: `shiver`, `wobble`, `jitter`, `bounce`, `bob`, `wave` (defined in `src/assets/css/global/utilities/text-animations.css`)
|
||
Safelist these classes in `tailwind.config.js` so they're not purged.
|
||
|
||
## Project-Specific Patterns
|
||
|
||
### Collections
|
||
- **`allPosts`**: All markdown files in `src/posts/**/*.md` (reversed chronological)
|
||
- **`tagList`**: Unique tags across all content (excludes 'posts', 'docs', 'all', 'mix', 'project')
|
||
- **`goPages`**: Pages with `go:` frontmatter for URL redirects (output to `_redirects`)
|
||
- **`tracksByProject`**: Tracks grouped by project, sorted by track_number
|
||
|
||
### WebC Components
|
||
Located in `src/_includes/webc/`:
|
||
- `<custom-card>` - Card layout with slots (image, headline, date, tag, content, footer)
|
||
- `<custom-masonry>` - Masonry grid (with optional `layout="50-50"`)
|
||
- `<custom-details>` - Enhanced `<details>` with expand/collapse all buttons
|
||
|
||
### Turbo Drive Navigation
|
||
- Enabled for track-to-track navigation (no full page reload)
|
||
- `<turbo-frame id="main-content">` in layouts wraps main content
|
||
- Import Turbo in defer bundle: `{% js "defer" %} import * as Turbo from '/assets/components/turbo.js'; {% endjs %}`
|
||
|
||
### Pagination
|
||
Defined in `src/pages/blog.njk` with `pagination.size`. Adjust labels in `src/_data/meta.js` (`blog.paginationLabel`, etc.).
|
||
|
||
### Go URLs / Redirects
|
||
Add `go: /short-url` to page frontmatter. The `goPages` collection generates `_redirects` file for Netlify/Vercel.
|
||
|
||
## Common Issues & Solutions
|
||
|
||
### CSS Not Updating
|
||
CSS is built in `eleventy.before` event, not by Eleventy's passthrough copy. Changes to `src/assets/css/**/*.css` trigger rebuild via watch targets.
|
||
|
||
### Images Not Processing
|
||
Ensure image paths start with `/` (relative to `src/`). HTML Transform plugin processes existing `<img>` tags in output HTML.
|
||
|
||
### New Color Not Available
|
||
Run `npm run colors` after editing `colorsBase.json`. Update `src/assets/css/global/base/variables.css` if color names change.
|
||
|
||
### Tailwind Class Not Generated
|
||
Check if the utility is enabled in `tailwind.config.js` theme (only `backgroundColor`, `textColor`, `margin`, `padding` enabled by default). Add more if needed.
|
||
|
||
### Track Navigation Broken
|
||
Verify `project` frontmatter matches across tracks. Collection sorts by `track_number` (ensure it's a number, not string).
|
||
|
||
## File Conventions
|
||
|
||
### This project uses ESM syntax instead of CommonJS. Configurations are structured into separate modules in `src/_config` and are then imported into the main configuration file.
|
||
|
||
- **collections.js**: Manages Eleventy collections such as posts and tags: https://www.11ty.dev/docs/collections/
|
||
- **events.js**: For code that should run at certain times during the compiling process: https://www.11ty.dev/docs/events/
|
||
- **filters.js**: Used within templating syntax to transform data into a more presentable format: https://www.11ty.dev/docs/filters/
|
||
- **plugins.js**: Everything I or Eleventy considers to be a plugin: https://www.11ty.dev/docs/plugins/
|
||
- **shortcodes.js**: Defines shortcodes for reusable content: https://www.11ty.dev/docs/shortcodes/
|
||
|
||
Each configuration category (filters, plugins, shortcodes, etc.) is modularized. or example, `dates.js` within the `filters` folder contains date-related filters.
|
||
|
||
```js
|
||
import dayjs from 'dayjs';
|
||
|
||
export const toISOString = dateString => dayjs(dateString).toISOString();
|
||
export const formatDate = (date, format) => dayjs(date).format(format);
|
||
```
|
||
|
||
These individual modules are then imported and consolidated in a central `filters.js` file, which exports all the filters as a single default object.
|
||
|
||
```js
|
||
import {toISOString, formatDate} from './filters/dates.js';
|
||
// more imports
|
||
|
||
export default {
|
||
toISOString,
|
||
formatDate,
|
||
// more exports
|
||
};
|
||
|
||
```
|
||
|
||
### Integration in Eleventy Config
|
||
|
||
In the main Eleventy configuration file (`eleventy.config.js`), these modules are imported:
|
||
|
||
```js
|
||
import filters from './src/_config/filters.js';
|
||
import shortcodes from './src/_config/shortcodes.js';
|
||
```
|
||
|
||
They are then used to register filters and shortcodes with Eleventy, using this nice concise syntax:
|
||
|
||
```js
|
||
eleventyConfig.addFilter('toIsoString', filters.toISOString);
|
||
eleventyConfig.addFilter('formatDate', filters.formatDate);
|
||
// More filters...
|
||
eleventyConfig.addShortcode('svg', shortcodes.svgShortcode);
|
||
```
|
||
|
||
This method hopefully keeps the Eleventy config clean and focused, only concerning itself with the registration of functionalities, while the logic and definition remain abstracted in their respective modules.
|
||
|
||
|
||
# CSS
|
||
|
||
We are using Tailwind CSS to generate utility classes on demand, based on our design tokens. Add and delete your globally available custom block stylesheets in `src/assets/css/global/blocks/*.css`.
|
||
|
||
The methodology used is [CUBE CSS.](https://cube.fyi/)
|
||
|
||
If you have a look at the tailwind.config.js, you can see how that is done. For example, we are deactivating Tailwinds default reset.
|
||
|
||
We are hooking into the components layer, to make Tailwind output classes based on our tokens, instead of their default design system.
|
||
|
||
That is, you are able to use mt-xs-s instead of a class like mt-20 for example. Same goes for colors, depending on the namesin your colors.json, you get custom classes like text-pink. These use the usual Tailwind prefixes (see docs to learn how to generate colors).
|
||
|
||
You get a custom property mapped to the color name --color-my-custom-color-name and the classes bg-my-custom-color-name as well as text-my-custom-color-name.
|
||
|
||
Consider that we limit those utilities in the theme section:
|
||
|
||
```js
|
||
backgroundColor: ({theme}) => theme('colors'),
|
||
textColor: ({theme}) => theme('colors'),
|
||
margin: ({theme}) => ({ auto: 'auto', ...theme('spacing')}),
|
||
padding: ({theme}) => theme('spacing')
|
||
```
|
||
If you want to add the generation for border-color classes for example, you’d have to add that right there:
|
||
|
||
{% raw %}
|
||
```js
|
||
borderWidth: ({theme}) => theme('borderWidth'),
|
||
borderColor: ({theme}) => theme('colors')
|
||
```
|
||
{% endraw %}
|
||
|
||
Also. you do have something like md:text-right available because we define the screens (src/_data/designTokens/viewports.json):
|
||
|
||
{% raw %}
|
||
```js
|
||
screens: {
|
||
ltsm: {max: `${viewportTokens.sm}px`},
|
||
sm: `${viewportTokens.sm}px`,
|
||
md: `${viewportTokens.md}px`,
|
||
navigation: `${viewportTokens.navigation}px`
|
||
},
|
||
```
|
||
{% endraw %}
|
||
|
||
Additionally, you get custom properties based on the naming of your design token files:
|
||
|
||
{% raw %}
|
||
```js
|
||
const groups = [
|
||
{key: 'colors', prefix: 'color'},
|
||
{key: 'borderRadius', prefix: 'border-radius'},
|
||
{key: 'spacing', prefix: 'space'},
|
||
{key: 'fontSize', prefix: 'size'},
|
||
{key: 'lineHeight', prefix: 'leading'},
|
||
{key: 'fontFamily', prefix: 'font'},
|
||
{key: 'fontWeight', prefix: 'font'}
|
||
];
|
||
```
|
||
{% endraw %}
|
||
|
||
The clampGenerator generates Utopia-like CSS clamp values for fluid type and space and the tokensToTailwind function converts whatever format the project’s design tokens are in, into Tailwind friendly configuration objects.
|
||
|
||
## module.export
|
||
|
||
We don’t let Tailwind create all the utilities that it can do at the core. Each of the single keyword properties like fontSize are defined at the top of the file by running through the tokensToTailwind function, so they get referenced straight in the config.
|
||
|
||
The Tailwind media query function — screen — is very rarely used, but we at least want that rigged up to design tokens.
|
||
|
||
```js
|
||
// Prevents Tailwind's core components
|
||
blocklist: ['container'],
|
||
```
|
||
|
||
|
||
Container is not needed because there’s a wrapper in css/compositions.
|
||
|
||
```js
|
||
|
||
experimental: {
|
||
optimizeUniversalDefaults: true
|
||
},
|
||
```
|
||
|
||
This config value also contributes to getting rid of the massive wall of Custom Properties.
|
||
|
||
## plugins
|
||
|
||
{% raw %}
|
||
```js
|
||
|
||
plugin(function ({addComponents, config}) {
|
||
let result = '';
|
||
|
||
const currentConfig = config();
|
||
|
||
const groups = [
|
||
{key: 'colors', prefix: 'color'},
|
||
{key: 'spacing', prefix: 'space'},
|
||
{key: 'fontSize', prefix: 'size'},
|
||
{key: 'fontLeading', prefix: 'leading'},
|
||
{key: 'fontFamily', prefix: 'font'},
|
||
{key: 'fontWeight', prefix: 'font'}
|
||
];
|
||
|
||
groups.forEach(({key, prefix}) => {
|
||
const group = currentConfig.theme[key];
|
||
|
||
if (!group) {
|
||
return;
|
||
}
|
||
|
||
Object.keys(group).forEach(key => {
|
||
result += `--${prefix}-${key}: ${group[key]};`;
|
||
});
|
||
});
|
||
|
||
addComponents({
|
||
':root': postcssJs.objectify(postcss.parse(result))
|
||
});
|
||
}),
|
||
```
|
||
{% endraw %}
|
||
|
||
Right, this is the sort of thing that sold me on Tailwind. They have a whole custom plugin system that you can tap into. One thing I want to always do is generate a nice block of Custom Properties, based on design tokens. That’s exactly what the above code does.
|
||
|
||
It goes through each defined group (groups) and then grabs the values > generates Custom Property values > sticks them to the result.
|
||
|
||
Finally, using postcssJs and postcss, an object that Tailwind can understand is created. The addComponents function sticks that custom properties block on the @components layer. It’s a bit of a hack, but it does the job.
|
||
|
||
{% raw %}
|
||
```js
|
||
|
||
plugin(function ({addUtilities, config}) {
|
||
const currentConfig = config();
|
||
const customUtilities = [
|
||
{key: 'spacing', prefix: 'flow-space', property: '--flow-space'},
|
||
{key: 'spacing', prefix: 'region-space', property: '--region-space'},
|
||
{key: 'spacing', prefix: 'gutter', property: '--gutter'}
|
||
];
|
||
|
||
customUtilities.forEach(({key, prefix, property}) => {
|
||
const group = currentConfig.theme[key];
|
||
|
||
if (!group) {
|
||
return;
|
||
}
|
||
|
||
Object.keys(group).forEach(key => {
|
||
addUtilities({
|
||
[`.${prefix}-${key}`]: postcssJs.objectify(
|
||
postcss.parse(`${property}: ${group[key]}`)
|
||
)
|
||
});
|
||
});
|
||
});
|
||
})
|
||
```
|
||
{% endraw %}
|
||
|
||
This function creates custom utilities that can be used in markup, such as gutter-m or flow-space-s. It’s all rigged up to the design tokens Custom Property block. It’s damn useful, especially when tweaking layout compositions in context.
|
||
|
||
## PostCSS
|
||
|
||
{% raw %}
|
||
```js
|
||
|
||
@import 'tailwindcss/base';
|
||
|
||
@import 'global/reset.css';
|
||
@import 'global/fonts.css';
|
||
|
||
@import 'tailwindcss/components';
|
||
|
||
@import 'global/variables.css';
|
||
@import 'global/global-styles.css';
|
||
|
||
@import-glob 'blocks/*.css';
|
||
@import-glob 'compositions/*.css';
|
||
@import-glob 'utilities/*.css';
|
||
|
||
@import 'tailwindcss/utilities';
|
||
|
||
```
|
||
{% endraw %}
|
||
I try to maintain a decent source order for specificity purposes as you can see. The @import 'tailwindcss/components' is where that block of Custom Properties generated in the Tailwind config gets put. Because everything else that layer does is disabled in config, it’s nice and clean.
|
||
|
||
The CUBE parts are all imported using the extremely useful import-glob PostCSS plugin. This allows new files to be added to directories and imported straight away.
|
||
|
||
|
||
### Inline CSS and bundles
|
||
|
||
The main CSS file is now inline in production to improve performance, see `.src/_includes/head/css-inline.njk`.
|
||
|
||
You can add per-page or component bundles of CSS. Instead of adding your CSS file to the `src/assets/css/global/blocks/` directory, you can place them in `src/assets/css/local/`. All CSS files in there will be stored alongside `global.css` in `.src/_includes/css/`. You can now include them in the "local" bundle only on pages or components where you need them:
|
||
|
||
{% raw %}
|
||
|
||
```jinja2
|
||
{% css "local" %}
|
||
{% include "css/your-stylesheet.css" %}
|
||
{% endcss %}
|
||
```
|
||
|
||
{% endraw %}
|
||
|
||
### Component CSS
|
||
|
||
All CSS files placed in `src/assets/css/components/` will be sent to the output folder, where components can reference them: `/assets/css/components/*.css`.
|
||
|
||
### Debugging CSS
|
||
|
||
In `src/assets/css/global.css` you can decomment `@import-glob 'tests/*.css';` to include CSS for debugging.
|
||
|
||
It makes visible when your code[ wrapped in `<is-land>` elements](https://github.com/11ty/is-land) is being hydrated, where things might overflow and many other warnings and errors [that Heydon Pickering came up with](https://heydonworks.com/article/testing-html-with-modern-css/).
|
||
|
||
### Cascade layers
|
||
|
||
We now use cascade layers! Up until now, I used the `:where()` pseudo-class to create low specificity selectors for the reset and compositions. [Mayank inspired me](https://mayank.co/blog/css-reset-layer/) to change to cascade layers. We have two major bundles of CSS: everything included in "global" In `src/assets/css/global/global.css` is now organized in cascade layers. The "local" bundle is for per-page or component CSS, and does not use cascade layers - it has thus a higher specificity, independent of any selector specificity in the global CSS.
|
||
|
||
|
||
## Design Tokens
|
||
|
||
Edit all your preferences (colors, fluid text sizes etc.) in `src/_data/designTokens/*.json`.
|
||
|
||
Additional colors, variants and gradients for custom properties are automatically created in `src/assets/css/global/base/variables.css` based on the colors set in `colors.json`.
|
||
|
||
In the [style guide](/styleguide/) you can see how everything turns out.
|
||
|
||
### Special case: colors
|
||
|
||
As of version 4.0, you can create colors dynamically. Run `npm run colors` after setting your color values in `src/_data/designTokens/colorsBase.json`. This will create / overwrite the required `colors.json` file in the same directory. These colors become custom properties (e.g. `--color-gray-100`) and utility classes similar to the Tailwind CSS syntax (for example `bg-gray-100`, `text-gray-900`).
|
||
|
||
If you want to adjust how the colors turn out, edit `src/_config/setup/create-colors.js`.
|
||
|
||
Colors placed under `shades_neutral` or `shades_vibrant` are converted into scalable palettes. `shades_neutral` is better for grayish / monochromatic colors, while `shades_vibrant` is better for colorful palettes. Colors listed under `standalone` and `light_dark` are left as they are, `light_dark` items output a second "subdued" version optimized for dark themes.
|
||
|
||
```js
|
||
// this creates a palette with shades of green, 100 to 900
|
||
"shades_vibrant": [
|
||
{
|
||
"name": "green",
|
||
"value": "#008000"
|
||
}
|
||
],
|
||
```
|
||
|
||
Important: If you change the color names, you must edit `src/assets/css/global/base/variables.css` with your color names. The rest of the CSS files should only reference custom properties set in `variables.css`.
|
||
|
||
|
||
# Javascript
|
||
|
||
This project uses ESM syntax instead of CommonJS. Configurations are structured into separate modules in `src/_config` and are then imported into the main configuration file.
|
||
|
||
There are two kinds of bundles for JavaScript in this starter, see `.src/_includes/head/js-inline.njk` and `.src/_includes/head/js-defer.njk`.
|
||
By default, I include Eleventy's [is-land](https://github.com/11ty/is-land) framework and the theme toggle inline.
|
||
|
||
You can include more scripts like so:
|
||
|
||
{% raw %}
|
||
|
||
```jinja2
|
||
{% js "inline" %}
|
||
{% include "scripts/your-inline-script.js" %}
|
||
{% endjs %}
|
||
```
|
||
|
||
{% endraw %}
|
||
|
||
Same goes for scripts that should be defered:
|
||
|
||
{% raw %}
|
||
|
||
```jinja2
|
||
{% js "defer" %}
|
||
{% include "scripts/your-defered-script.js" %}
|
||
{% endjs %}
|
||
```
|
||
|
||
{% endraw %}
|
||
|
||
Scripts stored in `src/assets/scripts/components/` are sent to the output folder, while scripts in `src/assets/scripts/bundle/` are sent to `.src/_includes/scripts/`, from where you can include them in the respective bundle.
|
||
|
||
|
||
# Details
|
||
|
||
The `<custom-details>` WebC component has a corresponding Nunjucks include.
|
||
It uses the `<details>` and `<summary>` elements to create a collapsible section and enhances them aesthetically and functionally.
|
||
|
||
The JavaScript for the `<custom-details>` component adds functionality to buttons to expand and collapse the sections with one action. When JavaScript is disabled, the sections are still accessible and collapsible, but the extra buttons are hidden.
|
||
|
||
On page load, it checks if a hash corresponding to a details ID exists in the URL. If such an ID is found, the corresponding details section is programmatically opened, allowing direct navigation to an open section from a shared URL.
|
||
|
||
The sorting is set by default on "alphabetic", but you can also pass in "shuffle" or "reverse" as a parameter (directly in the `details.njk` partial).
|
||
|
||
### Usage
|
||
|
||
{% raw %}
|
||
```jinja2
|
||
{% set itemList = collections.docs %}
|
||
{% set headingLevel = "h2" %} {# optional, defaults to false #}
|
||
{% include 'partials/details.njk' %}
|
||
```
|
||
{% endraw %}
|
||
|
||
|
||
# Images
|
||
Using the [Eleventy Image](https://www.11ty.dev/docs/plugins/image/) plugin, there are three ways to handle image optimization: HTML Transform, Markdown syntax, and Nunjucks shortcodes. [See the dedicated blog post to dive (much) deeper.](/blog/post-with-an-image/)
|
||
|
||
Have a look at the [Attribute Overrides](https://www.11ty.dev/docs/plugins/image/#attribute-overrides) for the HTML Transform methods (1 and 2) for per instance overrides. Adding `eleventy:ignore` to an `<img>` element for example, skips this image.
|
||
|
||
### 1. HTML Transform
|
||
The HTML Transform automatically processes `<img>` and `<picture>` elements in your HTML files as a post-processing step during the build.
|
||
|
||
```html
|
||
<img src="./path/to/image.jpg" alt="alt text">
|
||
```
|
||
|
||
### 2. Markdown Syntax
|
||
|
||
The Markdown syntax creates the `<img>` element that the _HTML Transform plugin_ is looking for, and then transforms it to the `<picture>` element (if more than one format is set).
|
||
|
||
```markdown
|
||

|
||
```
|
||
|
||
### 3. Nunjucks Shortcodes
|
||
|
||
In Nunjucks templates you can also use shortcodes (`image` and `imageKeys`).
|
||
|
||
{% raw %}
|
||
|
||
```jinja2
|
||
{% image '/path/to/image.jpg', 'alt text' %}
|
||
{% imageKeys {
|
||
"alt": "alt text",
|
||
"src": "/path/to/image.jpg"
|
||
} %}
|
||
```
|
||
|
||
{% endraw %}
|
||
|
||
# Custom masonry
|
||
|
||
`<custom-masonry>` is designed to function as a masonry grid by dynamically adjusting item positions based on the available column space and the size of its content. The necessary JavaScript (`custom-masonry.js`) is loaded only once per component usage due to the `data-island="once"` attribute.
|
||
Optional: pass in `layout="50-50"` to set a 50% width for each column.
|
||
|
||
If no JavaScript is available, the grid will fall back to the regular grid layout defined in `src/assets/css/global/compositions/grid.css`.
|
||
|
||
```js
|
||
<custom-masonry> (children) </custom-masonry>
|
||
<custom-masonry layout="50-50"> (children) </custom-masonry>
|
||
```
|
||
|
||
# Navigation
|
||
|
||
Edit your navigation items in `src/_data/navigation.js`.
|
||
|
||
You have two options for mobile navigation: by default, the navigation on small displays is converted to small pills that wrap. This does not require any additional JavaScript.
|
||
|
||
### Drawer Menu
|
||
|
||
You can activate a drawer menu for mobile in `src/_data/meta.js`:
|
||
|
||
```js
|
||
navigation: {
|
||
// other settings
|
||
drawerNav: true,
|
||
},
|
||
```
|
||
|
||
`drawerNav` activates the navigation drawer, [built according to Manuel Matuzović's article on web.dev.](https://web.dev/articles/website-navigation)
|
||
|
||
Adjust your menu breakpoint in `src/_data/designTokens/viewports.json`
|
||
|
||
```json
|
||
{
|
||
// ...
|
||
"navigation": 662,
|
||
// ...
|
||
}
|
||
```
|
||
|
||
### Submenu
|
||
|
||
You can activate submenus in `src/_data/meta.js`:
|
||
|
||
```js
|
||
navigation: {
|
||
// other settings
|
||
subMenu: true,
|
||
},
|
||
```
|
||
|
||
This includes the JavaScript for the submenu functionality. Add your submenu items to `src/_data/navigation.js` using this structure:
|
||
|
||
```js
|
||
{
|
||
text: 'Unlinked parent',
|
||
url: '#',
|
||
submenu: [
|
||
{
|
||
text: 'Sub Item',
|
||
url: '/sub-item/'
|
||
},
|
||
... more items
|
||
]
|
||
},
|
||
```
|
||
|
||
# Pagination
|
||
|
||
The blog posts use [Eleventy's pagination feature](https://www.11ty.dev/docs/pagination/). The logic for this can be found in tha partial `src/_includes/partials/pagination.njk`, the layout `src/_layouts/blog.njk` includes it, how many entries should be on a page is defined in `src/pages/blog.md`.
|
||
|
||
If you do not want any pagination at all, it is easiest to set a very high number for the pagination size, for example:
|
||
|
||
```yaml
|
||
pagination:
|
||
data: collections.posts
|
||
size: 10000
|
||
```
|
||
|
||
In `src/_data_/meta.js` you can set some values for the visible content (previous / next buttons) and the aria labels.
|
||
|
||
You can also **hide the number fields** between the previous and next buttons by setting `paginationNumbers` to `false`.
|
||
|
||
```js
|
||
blog: {
|
||
// other adjustments
|
||
paginationLabel: 'Blog',
|
||
paginationPage: 'Page',
|
||
paginationPrevious: 'Previous',
|
||
paginationNext: 'Next',
|
||
paginationNumbers: true
|
||
}
|
||
```
|
||
|
||
If you want to change the collection that is paginated (by default `collections.posts`), you must do so in two places: the front matter of the template, `src/pages/blog.md`:
|
||
|
||
```yaml
|
||
pagination:
|
||
data: collections.posts
|
||
```
|
||
|
||
and where the pagination component is included: `src/_layouts/blog.njk`:
|
||
|
||
{% raw %}
|
||
|
||
```jinja2
|
||
<!-- set collection to paginate -->
|
||
{% set collectionToPaginate = collections.posts %}
|
||
<!-- if the number of items in the collection is greater than the number of items shown on one page -->
|
||
{% if collectionToPaginate.length > pagination.size %}
|
||
<!-- include pagination -->
|
||
{% include 'partials/pagination.njk' %}
|
||
{% endif %}
|
||
```
|
||
|
||
{% endraw %}
|
||
|
||
# Cards
|
||
|
||
WebC, we can use the custom element and opt in to the different slots.
|
||
Available slots:
|
||
|
||
image: has slot="image" on the container (picture or figure) by default)
|
||
headline: display the card's main title
|
||
date and tag: Grouped within the classes meta and cluster for date and tagging information
|
||
content
|
||
footer: for links or whatever footer information
|
||
|
||
I added some variants, avaliable via attribute selectors:
|
||
|
||
img-square: Enforces a square aspect ratio for images
|
||
clickable: Makes the whole card clickable
|
||
no-padding: Removes padding and background modifications
|
||
|
||
Usage
|
||
|
||
{% raw %}
|
||
```html
|
||
<custom-card>
|
||
{% image "path-to-img", "alt-text" %}
|
||
<span slot="date"></span>
|
||
<span slot="tag" class="button"></span>
|
||
<h2 slot="headline"></h2>
|
||
<p slot="content"></p>
|
||
<footer slot="footer"></footer>
|
||
</custom-card>
|
||
|
||
```
|
||
{% endraw %} |