├── .gitignore ├── .node-version ├── _source ├── 404.html ├── _data │ ├── env.js │ └── site.json ├── _includes │ ├── docs │ │ ├── forms.html │ │ └── theme-picker.html │ └── svg │ │ ├── grease.svg │ │ └── theme-picker.svg ├── _layouts │ └── base.html ├── _utilities │ ├── esbuild.js │ ├── fullDate.js │ ├── getRandom.js │ ├── groupBy.js │ ├── hasTag.js │ ├── image.js │ ├── lightningcss.js │ ├── markdownify.js │ ├── setVar.js │ └── style.js ├── assets │ ├── css │ │ ├── _base │ │ │ ├── @media.css │ │ │ ├── @root.css │ │ │ ├── _base.css │ │ │ ├── blocks.css │ │ │ ├── fonts.css │ │ │ ├── forms.css │ │ │ ├── overlays.css │ │ │ ├── themes.css │ │ │ └── typography.css │ │ ├── _components │ │ │ ├── _components.css │ │ │ ├── button.css │ │ │ ├── popover-menu.css │ │ │ └── small-details.css │ │ ├── _utilities │ │ │ ├── _utilities.css │ │ │ ├── animate.css │ │ │ ├── border.css │ │ │ ├── color.css │ │ │ ├── display.css │ │ │ ├── flex.css │ │ │ ├── grid.css │ │ │ ├── margin.css │ │ │ ├── opacity.css │ │ │ ├── padding.css │ │ │ ├── position.css │ │ │ ├── shadow.css │ │ │ └── text.css │ │ └── main.css │ ├── fonts │ │ └── Sono.woff2 │ ├── images │ │ └── icons │ │ │ ├── favicon-196x196.png │ │ │ ├── favicon-32x32.png │ │ │ └── favicon.svg │ └── js │ │ ├── _elements │ │ ├── animate-on-scroll.js │ │ ├── popover-menu.js │ │ ├── random-shapes.js │ │ ├── small-details.js │ │ └── theme-picker.js │ │ └── app.js ├── docs │ ├── docs.css │ ├── docs.json │ ├── index.html │ ├── instructions │ │ ├── 01-why-grease.md │ │ ├── 02-getting-started.md │ │ ├── 03-working-with-css.md │ │ ├── 04-working-with-js.md │ │ ├── 05-layout.md │ │ ├── 06-typography.md │ │ ├── 07-color.md │ │ ├── 08-animation.md │ │ └── instructions.json │ ├── kitchen-sink.html │ └── snippets │ │ ├── 01-typography.html │ │ ├── 02-popovers.html │ │ ├── 03-colors.html │ │ ├── 04-buttons.html │ │ ├── 05-forms.html │ │ ├── 06-grid.html │ │ ├── 07-themes.html │ │ └── snippets.json ├── index.html └── sitemap.liquid ├── biome.json ├── eleventy.config.js ├── netlify.toml ├── package-lock.json ├── package.json └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | .cache/ 2 | node_modules 3 | _public/ 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | 20.11.1 -------------------------------------------------------------------------------- /_source/404.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: 404 3 | layout: base.html 4 | --- 5 | 6 |
7 |

404

8 |

There’s nothing to see here.

9 |
10 | -------------------------------------------------------------------------------- /_source/_data/env.js: -------------------------------------------------------------------------------- 1 | // 11ty's current context 2 | const environment = process.env.ELEVENTY_ENV; 3 | const PROD_ENV = 'prod'; 4 | const STAGE_ENV = 'stage'; 5 | 6 | // the possible base URLs 7 | const prodUrl = process.env.URL; 8 | const stageUrl = process.env.DEPLOY_PRIME_URL; 9 | const devUrl = ''; 10 | 11 | // set the baseUrl according to the environment 12 | let baseUrl; 13 | if (environment === PROD_ENV) { 14 | baseUrl = prodUrl; 15 | } else if (environment === STAGE_ENV) { 16 | baseUrl = stageUrl; 17 | } else { 18 | baseUrl = devUrl; 19 | } 20 | 21 | // useful for env-specific template conditionals 22 | const isProduction = environment === PROD_ENV; 23 | const isStaging = environment === STAGE_ENV; 24 | 25 | // the current branch name and Netlify deploy context 26 | const branch = process.env.BRANCH; 27 | const context = process.env.CONTEXT; 28 | 29 | export { environment, isProduction, isStaging, baseUrl, branch, context }; 30 | -------------------------------------------------------------------------------- /_source/_data/site.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Grease", 3 | "description": "Grease takes the friction out of building fast, adaptable websites." 4 | } 5 | -------------------------------------------------------------------------------- /_source/_includes/docs/forms.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 8 | 22 |
23 | 24 | 28 | 29 |
30 |
31 | 35 | 39 | 42 |
43 | 44 |
45 |
46 | -------------------------------------------------------------------------------- /_source/_includes/docs/theme-picker.html: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /_source/_includes/svg/grease.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_source/_includes/svg/theme-picker.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /_source/_layouts/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 21 | 22 | {% if title %}{{ title | escape }} | {% endif %}{{ site.title | escape }} 23 | 24 | 25 | 26 | 27 | 28 | 29 | {%- if noindex -%} 30 | 31 | {%- endif -%} 32 | 33 | {%- if canonical -%} 34 | 35 | {%- else -%} 36 | 37 | {%- endif -%} 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | {%- if stylesheet -%} 51 | {%- for sheet in stylesheet -%} 52 | 53 | {%- endfor -%} 54 | {%- endif -%} 55 | 56 | 57 | {%- if javascript -%} 58 | {%- for script in javascript -%} 59 | 60 | {%- endfor -%} 61 | {%- endif -%} 62 | 63 | 64 | 65 | 66 | {{ content }} 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /_source/_utilities/esbuild.js: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | /* ---------------------------------------------------------------------------- 3 | process JS files with esbuild 4 | ---------------------------------------------------------------------------- */ 5 | import esbuild from 'esbuild'; 6 | 7 | export default function (eleventyConfig) { 8 | eleventyConfig.addTemplateFormats('js'); 9 | eleventyConfig.addExtension('js', { 10 | outputFileExtension: 'js', 11 | async compile(inputContent, inputPath) { 12 | const baseDir = path.basename(path.dirname(inputPath)); 13 | if (baseDir.startsWith('_')) { 14 | return undefined; 15 | } 16 | const result = await esbuild.build({ 17 | entryPoints: [inputPath], 18 | bundle: true, 19 | minify: true, 20 | sourcemap: true, 21 | format: 'esm', 22 | logLevel: 'warning', 23 | outdir: '_public/assets/js', 24 | outbase: '_source/assets/js', 25 | metafile: true, 26 | }); 27 | 28 | const files = []; 29 | const inputs = Object.values(result.metafile.inputs); 30 | for (const input of inputs) { 31 | const { imports } = input; 32 | if (imports.length) { 33 | for (const file of imports) { 34 | files.push(file.path); 35 | } 36 | } 37 | } 38 | this.addDependencies(inputPath, files); 39 | return () => result.js; 40 | }, 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /_source/_utilities/fullDate.js: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------- 2 | returns a formatted date - via https://stackoverflow.com/a/31615643 3 | Liquid: {{ page.date | fullDate }} 4 | ---------------------------------------------------------------------------- */ 5 | const appendSuffix = (n) => { 6 | const s = ['th', 'st', 'nd', 'rd']; 7 | const v = n % 100; 8 | return n + (s[(v - 20) % 10] || s[v] || s[0]); 9 | }; 10 | 11 | export default function fullDate(value) { 12 | const dateObject = new Date(value); 13 | const months = [ 14 | 'January', 15 | 'February', 16 | 'March', 17 | 'April', 18 | 'May', 19 | 'June', 20 | 'July', 21 | 'August', 22 | 'September', 23 | 'October', 24 | 'November', 25 | 'December', 26 | ]; 27 | const dayWithSuffix = appendSuffix(dateObject.getDate()); 28 | return `${months[dateObject.getMonth()]} ${dayWithSuffix} ${dateObject.getFullYear()}`; 29 | } 30 | -------------------------------------------------------------------------------- /_source/_utilities/getRandom.js: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------- 2 | return a random item from a collection, excluding the current page 3 | Liquid: {% assign foo = collections.bar | getRandom: page %} 4 | ---------------------------------------------------------------------------- */ 5 | export default function getRandom(items, avoid) { 6 | let selected = items[Math.floor(Math.random() * items.length)]; 7 | while (selected.url === avoid.url) { 8 | selected = items[Math.floor(Math.random() * items.length)]; 9 | } 10 | return selected; 11 | } 12 | -------------------------------------------------------------------------------- /_source/_utilities/groupBy.js: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------- 2 | group items in a collection by an arbitrary key 3 | Liquid: {% assign foo = collections.bar | groupBy: "fizz" %} 4 | ---------------------------------------------------------------------------- */ 5 | export default function groupBy(array, key) { 6 | const get = (entry) => key.split('.').reduce((acc, key) => acc[key], entry); 7 | const map = array.reduce((acc, entry) => { 8 | const value = get(entry); 9 | if (typeof acc[value] === 'undefined') { 10 | acc[value] = []; 11 | } 12 | acc[value].push(entry); 13 | return acc; 14 | }, {}); 15 | return Object.keys(map).reduce((acc, key) => { 16 | acc.push({ name: key, items: map[key] }); 17 | return acc; 18 | }, []); 19 | } 20 | -------------------------------------------------------------------------------- /_source/_utilities/hasTag.js: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------- 2 | returns true if an entry belongs to a specific collection 3 | Liquid: {% assign foo = entry.data.tags | hasTag: "bar" %} 4 | ---------------------------------------------------------------------------- */ 5 | export default function hasTag(tags, tag) { 6 | return tags.includes(tag); 7 | } 8 | -------------------------------------------------------------------------------- /_source/_utilities/image.js: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------- 2 | creates a responsive 3 | ---------------------------------------------------------------------------- */ 4 | import eleventyImage, { eleventyImagePlugin } from '@11ty/eleventy-img'; 5 | 6 | export default async function image( 7 | src, 8 | alt, 9 | cssClass = null, 10 | sizes = '90vw', 11 | loadingAttr = 'lazy', 12 | ) { 13 | const filePath = `_source/assets/images/${src}`; 14 | const metadata = await eleventyImage(filePath, { 15 | widths: [500, 1000, 1500, 2000, 2500, 3000], 16 | formats: ['webp'], 17 | urlPath: '/assets/images/', 18 | outputDir: './_public/assets/images/', 19 | sharpWebpOptions: { quality: 40 }, 20 | }); 21 | const format = metadata[Object.keys(metadata)[0]]; 22 | const data = format[format.length - 1]; 23 | 24 | return `${alt}`; 34 | } 35 | -------------------------------------------------------------------------------- /_source/_utilities/lightningcss.js: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | /* ---------------------------------------------------------------------------- 3 | process CSS with LightningCSS 4 | ---------------------------------------------------------------------------- */ 5 | import { Features, bundleAsync } from 'lightningcss'; 6 | 7 | export default function (eleventyConfig) { 8 | eleventyConfig.addTemplateFormats('css'); 9 | eleventyConfig.addExtension('css', { 10 | outputFileExtension: 'css', 11 | async compile(inputContent, inputPath) { 12 | const baseDir = path.basename(path.dirname(inputPath)); 13 | if (baseDir.startsWith('_')) { 14 | return undefined; 15 | } 16 | const files = []; 17 | const targets = { future: 1 }; // enables draft syntaxes 18 | const result = await bundleAsync({ 19 | filename: inputPath, 20 | minify: true, 21 | sourceMap: true, 22 | drafts: { customMedia: true }, 23 | resolver: { 24 | resolve(specifier, from) { 25 | const importPath = path.resolve(path.dirname(from), specifier); 26 | files.push(importPath); 27 | return path.resolve(path.dirname(from), specifier); 28 | }, 29 | }, 30 | targets, 31 | }); 32 | this.addDependencies(inputPath, files); 33 | return () => result.code.toString(); 34 | }, 35 | }); 36 | } 37 | -------------------------------------------------------------------------------- /_source/_utilities/markdownify.js: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------- 2 | renders Markdown into HTML 3 | Liquid: {{ foo | markdownify }} 4 | ---------------------------------------------------------------------------- */ 5 | import markdownIt from 'markdown-it'; 6 | 7 | export default function markdownify(value) { 8 | const mdIt = markdownIt({ 9 | html: true, 10 | typographer: true, 11 | }); 12 | return mdIt.render(value); 13 | } 14 | -------------------------------------------------------------------------------- /_source/_utilities/setVar.js: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------- 2 | pass content to a parent template in a slot-like fashion 3 | {% setVar 'foo' %}fizz{% endsetVar %} 4 | ---------------------------------------------------------------------------- */ 5 | export default function setVar(content, name) { 6 | this.page[name] = content; 7 | return ''; 8 | } 9 | -------------------------------------------------------------------------------- /_source/_utilities/style.js: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------- 2 | process CSS tag pair and add it to a 12 |