├── .gitignore ├── README.md ├── beneficiary.html ├── build └── build.js ├── icons ├── icon-alarm.svg ├── icon-cow.svg └── icon-wheelchair.svg ├── index.html ├── index.njk ├── package-lock.json ├── package.json ├── script └── enhance-icons.js ├── styles └── style.css └── sw.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Icons! Cached! 2 | 3 | I wrote that inlining your <svg> icons makes for the best icon system. Scott Jehl wrote that just because you inline something doesn't mean you can't cache it. Let's see if Scott's idea can extend to SVG icons. 4 | 5 | [Demo Site](https://serene-sinoussi-3d0c1f.netlify.com/) 6 | 7 | ## Needs a server... 8 | 9 | This works on macOS: 10 | 11 | ``` 12 | php -S localhost:2222 13 | ``` 14 | 15 | ## Build 16 | 17 | There is a wildly simple build step here: 18 | 19 | ``` 20 | node build/build.js 21 | ``` 22 | 23 | It's just so that the SVG icons can be included and not duplicated needlessly. 24 | -------------------------------------------------------------------------------- /beneficiary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Caching 6 | 7 | 8 | 9 | 10 | 11 |

Cached and Inline SVGs

12 | 13 |

I wrote that inlining your <svg> icons makes for the best icon system. Scott Jehl wrote that just because you inline something doesn't mean you can't cache it. Let's see if Scott's idea can extend to SVG icons.

14 | 15 |

Here's the GitHub Repo.

16 | 17 |

I'm the beneficiary!

18 | 19 |

This page benefits from the caching, where as this one cached the stuff.

20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |

You could definitely get fancier with this, with cookies or something to determine whether or not to display the inline SVG and cache it, or if you know they are cached and can show the <use> stuff. 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /build/build.js: -------------------------------------------------------------------------------- 1 | var nunjucks = require("nunjucks"); 2 | var fs = require("fs"); 3 | 4 | fs.writeFile("index.html", nunjucks.render("index.njk"), function(err, data) { 5 | if (err) console.log(err); 6 | console.log("Compiled the Nunjucks, captain."); 7 | }); 8 | -------------------------------------------------------------------------------- /icons/icon-alarm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /icons/icon-cow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /icons/icon-wheelchair.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Caching 6 | 7 | 8 | 9 | 10 | 11 |

Cached and Inline SVGs

12 | 13 |

I wrote that inlining your <svg> icons makes for the best icon system. Scott Jehl wrote that just because you inline something doesn't mean you can't cache it. Let's see if Scott's idea can extend to SVG icons.

14 | 15 |

Here's the GitHub Repo.

16 | 17 |

I'm doing the caching!

18 | 19 |

This page does the caching, whereas this one benefits from said caching.

20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /index.njk: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Caching 6 | 7 | 8 | 9 | 10 | 11 |

Cached and Inline SVGs

12 | 13 |

I wrote that inlining your <svg> icons makes for the best icon system. Scott Jehl wrote that just because you inline something doesn't mean you can't cache it. Let's see if Scott's idea can extend to SVG icons.

14 | 15 |

I'm doing the caching!

16 | 17 |

This page does the caching, whereas this one benefits from said caching.

18 | 19 |

Here's the GitHub Repo.

20 | 21 | {% include "icons/icon-wheelchair.svg" %} 22 | 23 | {% include "icons/icon-cow.svg" %} 24 | 25 | {% include "icons/icon-alarm.svg" %} 26 | 27 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cached-icons", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "a-sync-waterfall": { 8 | "version": "1.0.1", 9 | "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", 10 | "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==" 11 | }, 12 | "anymatch": { 13 | "version": "3.1.1", 14 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", 15 | "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", 16 | "optional": true, 17 | "requires": { 18 | "normalize-path": "^3.0.0", 19 | "picomatch": "^2.0.4" 20 | } 21 | }, 22 | "asap": { 23 | "version": "2.0.6", 24 | "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", 25 | "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" 26 | }, 27 | "binary-extensions": { 28 | "version": "2.1.0", 29 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", 30 | "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", 31 | "optional": true 32 | }, 33 | "braces": { 34 | "version": "3.0.2", 35 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 36 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 37 | "optional": true, 38 | "requires": { 39 | "fill-range": "^7.0.1" 40 | } 41 | }, 42 | "chokidar": { 43 | "version": "3.4.3", 44 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", 45 | "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", 46 | "optional": true, 47 | "requires": { 48 | "anymatch": "~3.1.1", 49 | "braces": "~3.0.2", 50 | "fsevents": "~2.1.2", 51 | "glob-parent": "~5.1.0", 52 | "is-binary-path": "~2.1.0", 53 | "is-glob": "~4.0.1", 54 | "normalize-path": "~3.0.0", 55 | "readdirp": "~3.5.0" 56 | } 57 | }, 58 | "commander": { 59 | "version": "5.1.0", 60 | "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", 61 | "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" 62 | }, 63 | "fill-range": { 64 | "version": "7.0.1", 65 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 66 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 67 | "optional": true, 68 | "requires": { 69 | "to-regex-range": "^5.0.1" 70 | } 71 | }, 72 | "fsevents": { 73 | "version": "2.1.3", 74 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", 75 | "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", 76 | "optional": true 77 | }, 78 | "glob-parent": { 79 | "version": "5.1.1", 80 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", 81 | "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", 82 | "optional": true, 83 | "requires": { 84 | "is-glob": "^4.0.1" 85 | } 86 | }, 87 | "is-binary-path": { 88 | "version": "2.1.0", 89 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 90 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 91 | "optional": true, 92 | "requires": { 93 | "binary-extensions": "^2.0.0" 94 | } 95 | }, 96 | "is-extglob": { 97 | "version": "2.1.1", 98 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 99 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 100 | "optional": true 101 | }, 102 | "is-glob": { 103 | "version": "4.0.1", 104 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 105 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 106 | "optional": true, 107 | "requires": { 108 | "is-extglob": "^2.1.1" 109 | } 110 | }, 111 | "is-number": { 112 | "version": "7.0.0", 113 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 114 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 115 | "optional": true 116 | }, 117 | "normalize-path": { 118 | "version": "3.0.0", 119 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 120 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 121 | "optional": true 122 | }, 123 | "nunjucks": { 124 | "version": "3.2.2", 125 | "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.2.tgz", 126 | "integrity": "sha512-KUi85OoF2NMygwODAy28Lh9qHmq5hO3rBlbkYoC8v377h4l8Pt5qFjILl0LWpMbOrZ18CzfVVUvIHUIrtED3sA==", 127 | "requires": { 128 | "a-sync-waterfall": "^1.0.0", 129 | "asap": "^2.0.3", 130 | "chokidar": "^3.3.0", 131 | "commander": "^5.1.0" 132 | } 133 | }, 134 | "picomatch": { 135 | "version": "2.2.2", 136 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", 137 | "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", 138 | "optional": true 139 | }, 140 | "readdirp": { 141 | "version": "3.5.0", 142 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", 143 | "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", 144 | "optional": true, 145 | "requires": { 146 | "picomatch": "^2.2.1" 147 | } 148 | }, 149 | "to-regex-range": { 150 | "version": "5.0.1", 151 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 152 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 153 | "optional": true, 154 | "requires": { 155 | "is-number": "^7.0.0" 156 | } 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cached-icons", 3 | "version": "1.0.0", 4 | "description": "Can we use the [inline-cache-both](https://www.filamentgroup.com/lab/inlining-cache.html) technique?", 5 | "main": "sw.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "nunjucks": "^3.2.2" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /script/enhance-icons.js: -------------------------------------------------------------------------------- 1 | const icons = document.querySelectorAll("svg.icon"); 2 | 3 | icons.forEach(icon => { 4 | const url = icon.querySelector("use").getAttribute("xlink:href"); // might wanna look for href also 5 | fetch(url) 6 | .then(response => response.text()) 7 | .then(data => { 8 | /* This is probably a bit layout-thrashy. Someone smarter than me could probably fix that up. */ 9 | 10 | // Replace the with inline SVG 11 | const newEl = document.createElement("span"); 12 | newEl.innerHTML = data; 13 | icon.parentNode.replaceChild(newEl, icon); 14 | 15 | // Remove the s 16 | const parent = newEl.parentNode; 17 | while (newEl.firstChild) parent.insertBefore(newEl.firstChild, newEl); 18 | parent.removeChild(newEl); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /styles/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | font: 100%/1.4 system-ui; 3 | max-width: 500px; 4 | margin: 0 auto; 5 | } 6 | 7 | h1 { 8 | font-weight: 200; 9 | } 10 | 11 | .icon { 12 | width: 24px; 13 | height: 24px; 14 | } 15 | -------------------------------------------------------------------------------- /sw.js: -------------------------------------------------------------------------------- 1 | self.addEventListener("fetch", event => { 2 | let request = event.request; 3 | 4 | event.respondWith( 5 | caches.match(request).then(response => { 6 | return response || fetch(request); 7 | }) 8 | ); 9 | }); 10 | --------------------------------------------------------------------------------