From 60f26c1cf6282948520b31608266f73035388e36 Mon Sep 17 00:00:00 2001 From: Jonny Gerig Meyer Date: Tue, 13 Jul 2021 18:59:41 -0400 Subject: [PATCH] Move image shortcode to its own file. --- .eleventy.js | 66 +--------------- content/_includes/embed.macros.njk | 3 +- content/blog/2016/cssconf.md | 32 ++++---- content/blog/2018/introducing-herman.md | 6 +- content/sample/images.md | 6 +- content/work/coachhub.md | 10 +-- jest.config.js | 2 +- src/filters/images.js | 100 ++++++++++++++++++++++++ 8 files changed, 130 insertions(+), 95 deletions(-) create mode 100644 src/filters/images.js diff --git a/.eleventy.js b/.eleventy.js index 0bac266a4..9553f0234 100644 --- a/.eleventy.js +++ b/.eleventy.js @@ -1,8 +1,5 @@ 'use strict'; -const path = require('path'); - -const image = require('@11ty/eleventy-img'); const rss = require('@11ty/eleventy-plugin-rss'); const syntaxHighlight = require('@11ty/eleventy-plugin-syntaxhighlight'); const yaml = require('js-yaml'); @@ -10,6 +7,7 @@ const _ = require('lodash'); const birds = require('#/birds'); const events = require('#/events'); +const images = require('#/images'); const pages = require('#/pages'); const tags = require('#/tags'); const taxonomy = require('#/taxonomy'); @@ -17,64 +15,6 @@ const time = require('#/time'); const type = require('#/type'); const utils = require('#/utils'); -// eleventy image plugin -const imageShortcode = (src, alt, attrs, sizes, getUrl) => { - const imgConfig = taxonomy.fromTaxonomy('img'); - const imgSizes = - sizes && imgConfig.sizes[sizes] - ? imgConfig.sizes[sizes] - : sizes || imgConfig.sizes.default; - - const options = { - widths: imgConfig.widths, - formats: imgConfig.formats, - urlPath: '/img/', - outputDir: './_site/img/', - sharpJpegOptions: { - progressive: true, - quality: 80, - }, - sharpWebpOptions: { - quality: 60, - nearLossless: true, - reductionEffort: 3, - }, - - // eslint-disable-next-line - filenameFormat: function (id, src, width, format, options) { - const extension = path.extname(src); - const name = path.basename(src, extension); - - return `${name}-${width}w.${format}`; - }, - }; - - // generate images, while this is async we don’t wait - image(src, options); - - // eslint-disable-next-line no-sync - const metadata = image.statsSync(src, options); - - if (getUrl) { - const data = metadata.jpeg[metadata.jpeg.length - 1]; - return data.url; - } - - const imageAttributes = _.merge( - { - alt, - sizes: imgSizes, - loading: 'lazy', - decoding: 'async', - }, - attrs, - ); - - return image.generateHTML(metadata, imageAttributes, { - whitespaceMode: 'inline', - }); -}; - module.exports = (eleventyConfig) => { eleventyConfig.setUseGitIgnore(false); eleventyConfig.setWatchThrottleWaitTime(100); @@ -202,7 +142,7 @@ module.exports = (eleventyConfig) => { eleventyConfig.addFilter('max', (array) => Math.max(...array)); eleventyConfig.addFilter('imgSrc', (src) => - imageShortcode(src, null, null, null, false), + images.image(src, null, null, null, true), ); // shortcodes @@ -212,7 +152,7 @@ module.exports = (eleventyConfig) => { eleventyConfig.addShortcode('getDate', (format) => time.getDate(time.now(), format), ); - eleventyConfig.addNunjucksShortcode('image', imageShortcode); + eleventyConfig.addNunjucksShortcode('image', images.image); // config eleventyConfig.setLibrary('md', type.mdown); diff --git a/content/_includes/embed.macros.njk b/content/_includes/embed.macros.njk index 3b90df18e..f8a149ff6 100644 --- a/content/_includes/embed.macros.njk +++ b/content/_includes/embed.macros.njk @@ -365,12 +365,11 @@ params: attrs={} ) -%} {%- if src -%} - {%- set config = 'img' | fromTaxonomy -%} {%- if not ('://' in src) -%} {%- set src = ('./src/images/' + src) -%} {%- image src, alt or "", attrs, sizes -%} {%- else -%} - {{ alt or '' }} + {{ alt or '' }} {%- endif -%} {%- endif -%} {%- endmacro %} diff --git a/content/blog/2016/cssconf.md b/content/blog/2016/cssconf.md index b733c8dd5..f6ee5bd8f 100644 --- a/content/blog/2016/cssconf.md +++ b/content/blog/2016/cssconf.md @@ -43,7 +43,7 @@ you have a chance to watch the videos. ## Creativity in Programming for Fun and Profit -{{ embed.img('blog/2016/cssconf/sd.jpg', 'Sarah Drasner speaking') }} +{{ embed.img('blog/2016/cssconf/sd.jpg', 'Sarah Drasner speaking') }} [Sarah Drasner] talked about programming as a creative act. There is more than one way to solve any given problem – and different solutions @@ -73,7 +73,7 @@ load of amazing and beautiful samples. ## No Bugs in Sight -{{ embed.img('blog/2016/cssconf/bj.jpg', 'Brian Jordan speaking') }} +{{ embed.img('blog/2016/cssconf/bj.jpg', 'Brian Jordan speaking') }} [Brian Jordan] talked about automated front-end testing, with tools like [PhantomJS]. He works with [code.org], a non-profit working to increase @@ -96,7 +96,7 @@ were a few philisophical points that stood out: ## Nativize Is the New Normalize -{{ embed.img('blog/2016/cssconf/jl.jpg', 'Jessica Lord speaking') }} +{{ embed.img('blog/2016/cssconf/jl.jpg', 'Jessica Lord speaking') }} [Jessica Lord] showed us how to use [Electron] to build native (Mac, Windows, Linux) desktop applications using Node, HTML, and CSS – with @@ -118,7 +118,7 @@ class="title-ref">cursor: pointer and "rubber-band" scrolling. ## Component-Based Style Reuse -{{ embed.img('blog/2016/cssconf/ph.jpg', 'Pete Hunt speaking') }} +{{ embed.img('blog/2016/cssconf/ph.jpg', 'Pete Hunt speaking') }} [Pete Hunt] gave the most controversial talk of the conference, exploring the advantages of in-line CSS generated by JavaScript, using @@ -159,7 +159,7 @@ processor: ## CSS4 Grid: True Layout Finally Arrives -{{ embed.img('blog/2016/cssconf/jk.jpg', 'Jen Kramer speaking') }} +{{ embed.img('blog/2016/cssconf/jk.jpg', 'Jen Kramer speaking') }} [Jen Kramer] provided an overview of past and present CSS layout techniques, and an introduction to the new [CSS Grid] feature (still @@ -193,7 +193,7 @@ can use it in production. ## Silky Smooth Animation with CSS -{{ embed.img('blog/2016/cssconf/wb.jpg', 'Will Boyd speaking') }} +{{ embed.img('blog/2016/cssconf/wb.jpg', 'Will Boyd speaking') }} [Will Boyd] provided guidelines for creating smooth animations in CSS. To avoid jank, you have to keep all animations and transitions at 60fps. @@ -237,7 +237,7 @@ initially try to achieve with other properties. ## Stop Thinking in Pixels -{{ embed.img('blog/2016/cssconf/kg.jpg', 'Keith Grant speaking') }} +{{ embed.img('blog/2016/cssconf/kg.jpg', 'Keith Grant speaking') }} [Keith J. Grant] argued for using a combination of `em` and `rem` values, instead of `px`, for sizing on the web. @@ -291,7 +291,7 @@ provide terrible results on their own. ## CSS Variables: var(--subtitle) -{{ embed.img('blog/2016/cssconf/lv.jpg', 'Lea Verou speaking') }} +{{ embed.img('blog/2016/cssconf/lv.jpg', 'Lea Verou speaking') }} [Lea Verou] demonstrated various ways to use native CSS variables (AKA CSS Custom Properties) – already available in all modern browsers aside @@ -388,7 +388,7 @@ There's so much more! I highly recommend watching the video. ## Creative Solutions for Creative Design Challenges with CSS and SVG -{{ embed.img('blog/2016/cssconf/ss.jpg', 'Sara Soueidan speaking') }} +{{ embed.img('blog/2016/cssconf/ss.jpg', 'Sara Soueidan speaking') }} [Sara Soueidan] was scheduled to talk about SVG, but talked instead about hacks that she has learned to appreciate while working on the @@ -399,7 +399,7 @@ talk is well worth the watch. So much good material in here! ## The Hateful Weight -{{ embed.img('blog/2016/cssconf/hh.jpg', 'Henri Helvetica speaking') }} +{{ embed.img('blog/2016/cssconf/hh.jpg', 'Henri Helvetica speaking') }} [Henri Helvetica] talked about optimizing page and image sizes for the web. Did you know mp4 videos have better performance than gif images? @@ -412,7 +412,7 @@ display. ## Sass Map Magic -{{ embed.img('blog/2016/cssconf/mia.jpg', 'Miriam speaking') }} +{{ embed.img('blog/2016/cssconf/mia.jpg', 'Miriam speaking') }} I showed a wide range of uses for the underused Sass "map" data type – from simple site theme configurations, to data storage, and complex @@ -425,7 +425,7 @@ functional programming. All the [slides] are online. ## Webpack and CSS -{{ embed.img('blog/2016/cssconf/zg.jpg', 'Zach Green speaking') }} +{{ embed.img('blog/2016/cssconf/zg.jpg', 'Zach Green speaking') }} [Zach Green] walked us through his [Webpack] setup. I missed most of this, recovering from my own talk. @@ -437,7 +437,7 @@ this, recovering from my own talk. ## It's Time To Ditch the Grid System -{{ embed.img('blog/2016/cssconf/eh.jpg', 'Emily Hayman speaking') }} +{{ embed.img('blog/2016/cssconf/eh.jpg', 'Emily Hayman speaking') }} [Emily Hayman] demonstrated the ins and outs of using flexbox to build "content-driven" layouts, instead of forcing our content into grid @@ -452,7 +452,7 @@ start. ## Bauhaus in the Browser -{{ embed.img('blog/2016/cssconf/jm.jpg', 'Justin McDowell speaking') }} +{{ embed.img('blog/2016/cssconf/jm.jpg', 'Justin McDowell speaking') }} [Justin McDowell] used CSS transforms, grids, and more to bring [Bauhaus]-inspired art and layouts to the browser. It's a fun and @@ -467,7 +467,7 @@ known as the "vertigo effect") in CSS. ## The Great SVG RetCon -{{ embed.img('blog/2016/cssconf/ab.jpg', 'Amelia Bellamy-Royds speaking') }} +{{ embed.img('blog/2016/cssconf/ab.jpg', 'Amelia Bellamy-Royds speaking') }} [Amelia Bellamy-Royds] gave us a full overview of changes in [SVG2], along with a history of SVG. This talk is packed full of useful @@ -481,7 +481,7 @@ information, if you are using SVG in any way. ## Coding is a Privilege -{{ embed.img('blog/2016/cssconf/ar.jpg', 'speaking') }} +{{ embed.img('blog/2016/cssconf/ar.jpg', 'speaking') }} [Alisha Ramos] closed out the conference with a rousing talk about diversity (and privilege!) in tech. A few take-aways: diff --git a/content/blog/2018/introducing-herman.md b/content/blog/2018/introducing-herman.md index d8c0a8b8c..908e04733 100644 --- a/content/blog/2018/introducing-herman.md +++ b/content/blog/2018/introducing-herman.md @@ -133,8 +133,8 @@ the source. When a pattern changes, all the edits can be made in a single location, so documentation is more likely to stay up-to-date. [{{ embed.img( - src="blog/2017/herman-intro/sassdoc.jpg", - alt="SassDoc screenshot", + src="blog/2017/herman-intro/sassdoc.jpg", + alt="SassDoc screenshot", attrs={"class":"extend-small img-border img-shadow" } ) }}][SassDoc] @@ -207,7 +207,7 @@ Herman provides display annotations for [colors] (`@colors`), [fonts] [{{ embed.img( src="blog/2017/herman-intro/sizes.jpg", alt="Herman size palettes", - attrs={"class":"extend-small img-border img-shadow"} + attrs={"class":"extend-small img-border img-shadow"} ) }}][sizes] In order to display that data, you will need to export all your Sass diff --git a/content/sample/images.md b/content/sample/images.md index 840a00616..f44561db0 100644 --- a/content/sample/images.md +++ b/content/sample/images.md @@ -1,16 +1,12 @@ --- title: Responsive Images -sub: Using nunjucks and markdown +sub: Using nunjucks --- -{% import 'utility.macros.njk' as utility %} {% import 'embed.macros.njk' as embed %} ## srcSet -### markdown: -![](/assets/images/blog/2016/trick-loops.jpg) - ### embed (no width): {{ embed.img( src='blog/2016/trick-loops.jpg' diff --git a/content/work/coachhub.md b/content/work/coachhub.md index 79de77755..12f57152b 100644 --- a/content/work/coachhub.md +++ b/content/work/coachhub.md @@ -227,7 +227,11 @@ allowing us to move quickly and efficiently. Integrating design and implementation helped us minimize time and money waste.
-
Starting with designs for the smallest, mobile screens sizes helped clarify and prioritize relevant information for each user.
+{{ embed.img( + src="work/coachhub/profiles.png", + attrs={"class":"extend-full"} +) }} +
Starting with designs for the smallest, mobile screens sizes helped clarify and prioritize relevant information for each user.
@@ -245,15 +249,11 @@ adjusting UX design along the way, ensuring that CoachHub would be useful and relevant to users and partners.
-<<<<<<< HEAD {{ embed.img( src="work/coachhub/calendars.jpg", attrs={"class":"extend-large"} ) }}
As we observed users navigating the app, it became clear that the appointment calendar needed distinct views and interactions for coaches and clients.
-======= -
As we observed users navigating the app, it became clear that the appointment calendar needed distinct views and interactions for coaches and clients.
->>>>>>> main
### Accessibility diff --git a/jest.config.js b/jest.config.js index cfa1ae59d..c406d2c0f 100644 --- a/jest.config.js +++ b/jest.config.js @@ -23,7 +23,7 @@ module.exports = { collectCoverage: true, // An array of glob patterns indicating a set of files for which coverage information should be collected - collectCoverageFrom: ['src/filters/**/*.js'], + collectCoverageFrom: ['src/filters/**/*.js', '!src/filters/images.js'], // The directory where Jest should output its coverage files coverageDirectory: 'coverage', diff --git a/src/filters/images.js b/src/filters/images.js new file mode 100644 index 000000000..d915cd2cb --- /dev/null +++ b/src/filters/images.js @@ -0,0 +1,100 @@ +'use strict'; + +const path = require('path'); + +const eleventyImg = require('@11ty/eleventy-img'); +const _ = require('lodash'); + +const { fromTaxonomy } = require('#/taxonomy'); + +const imgConfig = fromTaxonomy('img'); + +/* @docs +label: Responsive Images +category: File +*/ + +/* @docs +label: image +category: responsive images +note: Generate responsive image using eleventy img plugin +example: | + {%- image src, "alt text", {"class":"my-image"}, "media" -%} +params: + src: + type: string + alt: + type: string | none + default: none + attrs: + type: object + default: '{}' + sizes: + type: string | none + default: none + note: | + Only required for small images, since the default is 100vw. + See taxonomy data for named sizes + like "card", "media", and "gallery". + getUrl: + type: boolean | none + default: none + note: | + Returns url to largest jpeg image instead of full HTML +*/ +const image = (src, alt, attrs, sizes, getUrl) => { + const imgSizes = + sizes && imgConfig.sizes[sizes] + ? imgConfig.sizes[sizes] + : sizes || imgConfig.sizes.default; + + const options = { + widths: imgConfig.widths, + formats: imgConfig.formats, + urlPath: '/assets/images/', + outputDir: './_site/assets/images/', + sharpJpegOptions: { + progressive: true, + quality: 80, + }, + sharpWebpOptions: { + quality: 60, + nearLossless: true, + reductionEffort: 3, + }, + filenameFormat: (id, imgSrc, width, format) => { + const extension = path.extname(imgSrc); + const name = path.basename(imgSrc, extension); + return `${name}-${width}w.${format}`; + }, + }; + + // generate images; this is async but we don’t wait + eleventyImg(src, options); + + // eslint-disable-next-line no-sync + const metadata = eleventyImg.statsSync(src, options); + + if (getUrl) { + const data = metadata.jpeg[metadata.jpeg.length - 1]; + return data.url; + } + + const imageAttributes = _.merge( + { + alt, + sizes: imgSizes, + loading: 'lazy', + decoding: 'async', + }, + attrs, + ); + + return eleventyImg.generateHTML(metadata, imageAttributes, { + whitespaceMode: 'inline', + }); +}; + +module.exports = { + image, +};