├── .eleventy.js ├── .gitignore ├── .prettierrc ├── README.md ├── contributing.md ├── netlify.toml ├── package-lock.json ├── package.json ├── rollup.config.js └── src ├── _data ├── global.js ├── helpers.js ├── navigation.json ├── site.json ├── styleguide.js └── tokens.json ├── _includes ├── icons │ └── arrow.svg ├── layouts │ ├── archive.njk │ ├── base.njk │ ├── feed-item.njk │ ├── home.njk │ ├── page.njk │ └── post.njk └── partials │ ├── components │ ├── intro.njk │ ├── nav.njk │ ├── pagination.njk │ └── post-list.njk │ └── global │ ├── availability.njk │ ├── meta-info.njk │ ├── service-worker.js │ ├── site-foot.njk │ ├── site-head.njk │ └── third-party-comments.njk ├── _redirects ├── archive.md ├── assets └── captions │ └── client-screencast-example.vtt ├── feed ├── all.njk └── writing.njk ├── filters ├── date-filter.js ├── likes-filter.js ├── markdown-filter.js └── w3-date-filter.js ├── images ├── demo-image-1.jpg ├── demo-image-2.jpg ├── noise.webp └── social-share.jpg ├── index.md ├── js └── components │ └── theme-toggle.js ├── pages ├── 404.md ├── freelance-pals-code-of-conduct.md ├── now.md ├── pages.json ├── projects.md └── uses.md ├── posts ├── 2018-a-year-in-review.md ├── 2019-a-year-in-review.md ├── a-dive-into-freelance-life.md ├── a-double-launch-day.md ├── a-minimal-react-base-project.md ├── a-modern-css-reset.md ├── a-progressive-disclosure-component.md ├── a-short-note-on-my-social-activity.md ├── adding-webmentions-to-my-personal-site.md ├── break-out-of-the-echo-chamber.md ├── browser-diversity.md ├── burnout-a-long-recovery.md ├── burnout.md ├── bypass-service-worker-on-localhost.md ├── community.md ├── competing-by-mimicking.md ├── context-and-caveats.md ├── contraction-logger.md ├── create-a-responsive-grid-layout-with-no-media-queries-using-css-grid.md ├── create-a-semantic-break-out-button-to-make-an-entire-element-clickable.md ├── create-a-split-faux-container-layout-with-css-grid-and-flexbox.md ├── create-a-user-controlled-dark-or-light-mode.md ├── creating-a-full-bleed-css-utility.md ├── creating-an-aspect-ratio-css-utility.md ├── css-doesnt-suck.md ├── css-naked-day-2020.md ├── css-specifity-and-the-cascade.md ├── custom-property-controlled-fluid-type-sizing.md ├── eating-my-own-dog-food.md ├── every-layout-how-it-works.md ├── feed-page.md ├── flow-and-rhythm-in-css-with-stalfos.md ├── fluid-scale-and-tokens-a-match-made-in-heaven.md ├── freelance-pro-tip.md ├── front-end-challenges-club-is-moving-to-piccalilli.md ├── get-css-custom-property-value-with-javascript.md ├── hello-im-andy-and-im-addicted-to-twitter.md ├── honesty-is-the-best-policy.md ├── im-a-web-designer.md ├── im-warming-to-tailwind-css.md ├── introducing-developurrs.md ├── introducing-the-button-element.md ├── jamstack-ifttt-and-netlify-a-power-trio.md ├── keep-it-simple.md ├── keeping-it-simple-with-css-that-scales.md ├── keeping-it-simple-with-image-alt-text-for-accessibility.md ├── making-the-new-piccalilli-logo.md ├── my-first-month-as-a-freelancer.md ├── my-fractured-relationship-with-big-tech.md ├── new-baby-visiting-user-flow.md ├── no-more-anchor-buttons.md ├── open-letter-to-laurence-robertson-mp.md ├── piccalilli-is-changing.md ├── piccalilli-the-future.md ├── posts.json ├── progressive-overflow-management-with-a-scroll-track-utility.md ├── relative-sizing-with-em-units.md ├── screencasting-the-secret-sauce-for-less-time-wasted-in-meetings.md ├── shipping.md ├── so-long-hankchizljaw.md ├── the-extremely-loud-minority.md ├── the-if-theres-time-line.md ├── the-p-in-progressive-enhancement-stands-for-pragmatism.md ├── the-power-of-progressive-enhancement.md ├── the-power-of-self-publishing.md ├── this-blog-in-2020.md ├── tracking-my-time.md ├── two-simple-methods-to-vertically-and-horizontally-center-content-with-css.md ├── walking.md ├── week-notes-1.md ├── week-notes-10.md ├── week-notes-11.md ├── week-notes-12.md ├── week-notes-13.md ├── week-notes-14.md ├── week-notes-15.md ├── week-notes-2.md ├── week-notes-4.md ├── week-notes-5.md ├── week-notes-6.md ├── week-notes-7.md ├── week-notes-8.md ├── week-notes-9.md ├── weeknotes-3.md ├── well-being-on-twitter.md └── what-i-searched-for-in-a-day-of-fighting-with-php.md ├── scss ├── _config.scss ├── _highlight.scss ├── _theme.scss ├── _typography.scss ├── components │ ├── _availability.scss │ ├── _avatar-group.scss │ ├── _avatar.scss │ ├── _button.scss │ ├── _comments.scss │ ├── _heading-permalink.scss │ ├── _input-field.scss │ ├── _intro.scss │ ├── _nav.scss │ ├── _noise-wrapper.scss │ ├── _pagination.scss │ ├── _post-list.scss │ ├── _post.scss │ ├── _scroll-track.scss │ ├── _site-foot.scss │ ├── _site-head.scss │ ├── _skip-link.scss │ ├── _syntax-highlighting.scss │ └── _video-player.scss ├── global.scss ├── mixins │ └── _dark-mode.scss └── utilities │ ├── _desaturate.scss │ ├── _dot-bg.scss │ ├── _highlight.scss │ ├── _inner-wrapper.scss │ ├── _str-replace.scss │ ├── _visually-hidden.scss │ └── _wrapper.scss ├── service-worker.njk ├── shortcodes └── codepen.js ├── styleguide.njk ├── tags.njk ├── transforms ├── html-min-transform.js └── parse-transform.js └── utils └── minify.js /.eleventy.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | const rssPlugin = require('@11ty/eleventy-plugin-rss'); 4 | const syntaxHighlight = require('@11ty/eleventy-plugin-syntaxhighlight'); 5 | 6 | // Import filters 7 | const dateFilter = require('./src/filters/date-filter.js'); 8 | const likesFilter = require('./src/filters/likes-filter.js'); 9 | const markdownFilter = require('./src/filters/markdown-filter.js'); 10 | const w3DateFilter = require('./src/filters/w3-date-filter.js'); 11 | 12 | // Import transforms 13 | const htmlMinTransform = require('./src/transforms/html-min-transform.js'); 14 | const parseTransform = require('./src/transforms/parse-transform.js'); 15 | 16 | // Import shortcodes 17 | const codepen = require('./src/shortcodes/codepen.js'); 18 | 19 | // Import data files 20 | const site = require('./src/_data/site.json'); 21 | 22 | module.exports = function(config) { 23 | // Filters 24 | config.addFilter('dateFilter', dateFilter); 25 | config.addFilter('likesFilter', likesFilter); 26 | config.addFilter('markdownFilter', markdownFilter); 27 | config.addFilter('w3DateFilter', w3DateFilter); 28 | 29 | // Layout aliases 30 | config.addLayoutAlias('home', 'layouts/home.njk'); 31 | 32 | // Transforms 33 | config.addTransform('htmlmin', htmlMinTransform); 34 | config.addTransform('parse', parseTransform); 35 | 36 | // Shortcodes 37 | config.addShortcode('codepen', codepen); 38 | 39 | // Passthrough copy 40 | config.addPassthroughCopy('src/assets'); 41 | config.addPassthroughCopy('src/_redirects'); 42 | config.addPassthroughCopy('src/images'); 43 | config.addPassthroughCopy('src/js'); 44 | 45 | const now = new Date(); 46 | 47 | // Custom collections 48 | const livePosts = post => post.date <= now && !post.data.draft; 49 | const feedExcluded = post => !['weeknotes'].some(x => post.data.tags.includes(x)); 50 | 51 | config.addCollection('posts', collection => { 52 | return [ 53 | ...collection 54 | .getFilteredByGlob('./src/posts/*.md') 55 | .filter(livePosts) 56 | .filter(feedExcluded) 57 | ].reverse(); 58 | }); 59 | 60 | config.addCollection('postFeed', collection => { 61 | return [ 62 | ...collection 63 | .getFilteredByGlob('./src/posts/*.md') 64 | .filter(livePosts) 65 | .filter(feedExcluded) 66 | ] 67 | .reverse() 68 | .slice(0, site.maxPostsPerPage); 69 | }); 70 | 71 | config.addCollection('feed', collection => { 72 | const notes = collection.getFilteredByTag('notes'); 73 | const links = collection.getFilteredByTag('links'); 74 | return [...notes, ...links].sort((a, b) => { 75 | return new Date(b.date) - new Date(a.date); 76 | }); 77 | }); 78 | 79 | config.addCollection('sortedNotes', function(collection) { 80 | return collection.getFilteredByTag('notes').reverse(); 81 | }); 82 | 83 | config.addCollection('sortedLinks', function(collection) { 84 | return collection.getFilteredByTag('links').reverse(); 85 | }); 86 | 87 | config.addCollection('allFeed', function(collection) { 88 | const posts = collection.getFilteredByGlob('./src/posts/*.md').filter(livePosts); 89 | return [...posts].sort((a, b) => { 90 | return new Date(b.date) - new Date(a.date); 91 | }); 92 | }); 93 | 94 | // Plugins 95 | config.addPlugin(rssPlugin); 96 | config.addPlugin(syntaxHighlight); 97 | 98 | return { 99 | dir: { 100 | input: 'src', 101 | output: 'dist' 102 | }, 103 | passthroughFileCopy: true 104 | }; 105 | }; 106 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | npm-debug.* 3 | *.scssc 4 | *.log 5 | *.swp 6 | .DS_Store 7 | .sass-cache 8 | .env 9 | node_modules 10 | dist 11 | 12 | # Specifics 13 | 14 | # Hide design tokens 15 | src/scss/_tokens.scss 16 | 17 | # Hide compiled CSS 18 | src/_includes/assets/* 19 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | printWidth: 90, 3 | tabWidth: 2, 4 | singleQuote: true, 5 | bracketSpacing: false 6 | } 7 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | command = "npm run production" 3 | publish = "dist" 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hylia", 3 | "version": "0.4.0", 4 | "description": "A simple Eleventy starter kit to help you have a blog of your own", 5 | "main": "index.js", 6 | "dependencies": { 7 | "@11ty/eleventy": "^0.10.0", 8 | "@11ty/eleventy-plugin-rss": "^1.0.6", 9 | "@11ty/eleventy-plugin-syntaxhighlight": "^2.0.3", 10 | "@tbranyen/jsdom": "^13.0.0", 11 | "axios": "^0.19.0", 12 | "concurrently": "^4.1.0", 13 | "dotenv": "^8.0.0", 14 | "html-minifier": "^4.0.0", 15 | "json-to-scss": "^1.3.1", 16 | "sanitize-html": "^1.20.1", 17 | "sass": "^1.21.0", 18 | "slugify": "^1.3.4", 19 | "stalfos": "git://github.com/andybelldesign/stalfos.git#49f9e68a7a590b8739df2087f05901dcc6baa761" 20 | }, 21 | "devDependencies": { 22 | "chokidar-cli": "^1.2.2", 23 | "cross-env": "^5.2.0", 24 | "make-dir-cli": "^2.0.0", 25 | "prettier": "^1.18.2", 26 | "rollup": "^1.16.1", 27 | "rollup-plugin-commonjs": "^10.0.0", 28 | "rollup-plugin-json": "^4.0.0", 29 | "rollup-plugin-node-builtins": "^2.1.2", 30 | "rollup-plugin-node-resolve": "^5.0.3" 31 | }, 32 | "scripts": { 33 | "sass:tokens": "npx json-to-scss src/_data/tokens.json src/scss/_tokens.scss", 34 | "sass:process": "npm run sass:tokens && sass src/scss/global.scss src/_includes/assets/css/global.css --style=compressed", 35 | "start": "concurrently 'npm run sass:process -- --watch' 'npm run serve'", 36 | "serve": "cross-env ELEVENTY_ENV=development npx eleventy --serve", 37 | "production": "npm run sass:process && npx eleventy" 38 | }, 39 | "repository": { 40 | "type": "git", 41 | "url": "git+https://github.com/andybelldesign/personal-site-hylia.git" 42 | }, 43 | "keywords": [], 44 | "author": "", 45 | "license": "MIT", 46 | "bugs": { 47 | "url": "https://github.com/andybelldesign/personal-site-hylia/issues" 48 | }, 49 | "homepage": "https://github.com/andybelldesign/personal-site-hylia#readme" 50 | } 51 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | const builtins = require('rollup-plugin-node-builtins'); 2 | const commonjs = require('rollup-plugin-commonjs'); 3 | const nodeResolve = require('rollup-plugin-node-resolve'); 4 | const json = require('rollup-plugin-json'); 5 | 6 | export default { 7 | input: 'src/admin/util', 8 | output: { 9 | file: 'dist/admin/util.js', 10 | format: 'iife', 11 | name: 'previewUtil', 12 | }, 13 | plugins: [ 14 | builtins(), 15 | nodeResolve(), 16 | commonjs(), 17 | json(), 18 | ] 19 | }; 20 | -------------------------------------------------------------------------------- /src/_data/global.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | random() { 3 | const segment = () => { 4 | return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); 5 | }; 6 | return `${segment()}-${segment()}-${segment()}`; 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /src/_data/helpers.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | getNextHeadingLevel(currentLevel) { 3 | return parseInt(currentLevel, 10) + 1; 4 | }, 5 | getReadingTime(text) { 6 | const wordsPerMinute = 200; 7 | const numberOfWords = text.split(/\s/g).length; 8 | return Math.ceil(numberOfWords / wordsPerMinute); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /src/_data/navigation.json: -------------------------------------------------------------------------------- 1 | { 2 | "items": [ 3 | { 4 | "text": "Writing", 5 | "url": "/writing/", 6 | "rel": null 7 | }, 8 | { 9 | "text": "Projects", 10 | "url": "/projects/", 11 | "rel": null 12 | }, 13 | { 14 | "text": "RSS", 15 | "url": "https://hankchizljaw.com/feed/all.xml", 16 | "rel": null 17 | }, 18 | { 19 | "text": "Micro Blog", 20 | "url": "//microblog.hankchizljaw.com", 21 | "rel": "external" 22 | }, 23 | { 24 | "text": "Twitter", 25 | "url": "//twitter.com/hankchizljaw", 26 | "rel": "me" 27 | }, 28 | { 29 | "text": "CodePen", 30 | "url": "//codepen.io/andybelldesign", 31 | "rel": null 32 | }, 33 | { 34 | "text": "GitHub", 35 | "url": "//github.com/hankchizljaw", 36 | "rel": "me" 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /src/_data/site.json: -------------------------------------------------------------------------------- 1 | { 2 | "showThemeCredit": true, 3 | "name": "Andy Bell", 4 | "availability": "I produce front-end development tutorials over at [Front-End Challenges Club](//front-end-challenges.club) and [Piccalilli](//piccalil.li). You can sign up for updates on Piccalilli to stay up to date with its progress.", 5 | "shortDesc": "Freelance web designer ➤ CSS stuff ➤ Curates piccalil.li ➤ Co-author of [Every Layout](https://every-layout.dev) ➤ Writing [CSS From Scratch](https://cssfromscratch.com)", 6 | "url": "https://hankchizljaw.com", 7 | "authorEmail": "me@andy-bell.design", 8 | "authorHandle": "@hankchizljaw", 9 | "authorName": "Andy Bell", 10 | "enableThirdPartyComments": false, 11 | "maxPostsPerPage": 5 12 | } 13 | -------------------------------------------------------------------------------- /src/_data/styleguide.js: -------------------------------------------------------------------------------- 1 | const tokens = require('./tokens.json'); 2 | 3 | module.exports = { 4 | colors() { 5 | let response = []; 6 | 7 | Object.keys(tokens.colors).forEach(key => { 8 | response.push({ 9 | value: tokens.colors[key], 10 | key 11 | }); 12 | }); 13 | 14 | return response; 15 | }, 16 | sizes() { 17 | let response = []; 18 | 19 | Object.keys(tokens['size-scale']).forEach(key => { 20 | response.push({ 21 | value: tokens['size-scale'][key], 22 | key 23 | }); 24 | }); 25 | 26 | return response; 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /src/_data/tokens.json: -------------------------------------------------------------------------------- 1 | { 2 | "size-scale": { 3 | "base": "1rem", 4 | "300": "0.8rem", 5 | "500": "1.25rem", 6 | "600": "1.56rem", 7 | "700": "1.95rem", 8 | "800": "2.44rem", 9 | "900": "3.05rem", 10 | "max": "4rem" 11 | }, 12 | "colors": { 13 | "dark": "#252525", 14 | "light": "#dedede" 15 | }, 16 | "fonts": { 17 | "base": "\"Georgia, serif\"", 18 | "sans": "\"'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif\"" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/_includes/icons/arrow.svg: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /src/_includes/layouts/archive.njk: -------------------------------------------------------------------------------- 1 | {% extends 'layouts/base.njk' %} 2 | {% set pageType = 'Post Archive' %} 3 | 4 | {# Intro content #} 5 | {% set introHeading = title %} 6 | {% set introSummary %}{{ content | safe }}{% endset %} 7 | 8 | {# Post list content #} 9 | {% set postListHeading = 'All posts' %} 10 | {% set postListItems = collections.posts %} 11 | 12 | {% block content %} 13 |
14 | {% include "partials/components/intro.njk" %} 15 | {% include "partials/components/post-list.njk" %} 16 |
17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /src/_includes/layouts/base.njk: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | {% include "partials/global/meta-info.njk" %} 13 | 14 | 15 | 16 | {% block head %} 17 | {% endblock %} 18 | 19 | 20 | {% include "partials/global/site-head.njk" %} 21 | {% block content %} 22 | {% endblock content %} 23 | {% include "partials/global/site-foot.njk" %} 24 | {% block foot %} 25 | {% endblock %} 26 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/_includes/layouts/feed-item.njk: -------------------------------------------------------------------------------- 1 | {% extends 'layouts/base.njk' %} 2 | {% set pageType = 'Feed item' %} 3 | 4 | {# Intro content #} 5 | {% set introHeading = title %} 6 | {% set introSummary %} 7 |

8 | {% if date %} 9 | 10 | {% endif %} 11 |

12 | {% endset %} 13 | 14 | {% block content %} 15 |
16 |
17 | {% include "partials/components/intro.njk" %} 18 |
19 | {{ content | safe }} 20 | {% if image %} 21 |

22 | {{ alt }} 23 |

24 | {% endif %} 25 | {% if linkUrl %} 26 |

{{ linkUrl }}

27 | {% endif %} 28 |
29 | {% include "partials/global/webmentions.njk" %} 30 | {% include "partials/global/availability.njk" %} 31 | {% if site.enableThirdPartyComments %} 32 |
33 | 36 | {% endif %} 37 | {% if tags %} 38 | 52 | {% endif %} 53 |
54 |
55 | {% endblock %} 56 | 57 | {{ content | safe }} 58 | -------------------------------------------------------------------------------- /src/_includes/layouts/home.njk: -------------------------------------------------------------------------------- 1 | {% extends 'layouts/base.njk' %} 2 | {% set pageType = 'Homepage' %} 3 | 4 | {# Intro content #} 5 | {% set introHeading = title %} 6 | {% set introSummary %} 7 | {{ content | safe }} 8 |

9 | Hire me 10 |

11 | {% endset %} 12 | 13 | {# Post list content #} 14 | {% set postListHeading = postsHeading %} 15 | {% set postListItems = collections.postFeed %} 16 | 17 | {# Archive link #} 18 | {% if postListItems.length < collections.posts.length %} 19 | {% set paginationNextText = archiveButtonText %} 20 | {% set paginationNextUrl = '/writing' %} 21 | {% endif %} 22 | 23 | {% block content %} 24 |
25 | {% include "partials/components/intro.njk" %} 26 | {% include "partials/components/post-list.njk" %} 27 | {% include "partials/components/pagination.njk" %} 28 |
29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /src/_includes/layouts/page.njk: -------------------------------------------------------------------------------- 1 | {% extends 'layouts/base.njk' %} 2 | {% set pageType = 'Page' %} 3 | 4 | {# Intro content #} 5 | {% set introHeading = title %} 6 | 7 | {% block content %} 8 |
9 |
10 | {% include "partials/components/intro.njk" %} 11 |
12 | {{ content | safe }} 13 |
14 |
15 |
16 | {% endblock %} 17 | 18 | {{ content | safe }} 19 | -------------------------------------------------------------------------------- /src/_includes/layouts/post.njk: -------------------------------------------------------------------------------- 1 | {% extends 'layouts/base.njk' %} 2 | {% set pageType = 'Post' %} 3 | 4 | {% set bodyModifier = '' %} 5 | 6 | {% if presentation %} 7 | {% set bodyModifier = 'post__body--presentation' %} 8 | {% endif %} 9 | 10 | {# Intro content #} 11 | {% set introHeading = title %} 12 | {% set introSummary %} 13 |

14 | {% if date %} 15 | 16 | {% endif %} 17 | — {{ helpers.getReadingTime(content) }} minute read 18 |

19 | {% endset %} 20 | 21 | {% block content %} 22 |
23 |
24 | {% include "partials/components/intro.njk" %} 25 |
26 | {{ content | safe }} 27 |
28 | {% include "partials/global/availability.njk" %} 29 | {% if site.enableThirdPartyComments %} 30 |
31 | 34 | {% endif %} 35 | {% if tags %} 36 | 50 | {% endif %} 51 |
52 |
53 | {% endblock %} 54 | {% block foot %} 55 | {% if hasCodePen %} 56 | 57 | {% endif %} 58 | {% endblock %} 59 | {{ content | safe }} 60 | -------------------------------------------------------------------------------- /src/_includes/partials/components/intro.njk: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{ introHeading }}

4 | {% if introSummary %} 5 |
{{ introSummary | safe }}
6 | {% endif %} 7 |
8 |
9 | -------------------------------------------------------------------------------- /src/_includes/partials/components/nav.njk: -------------------------------------------------------------------------------- 1 | {% if navigation.items %} 2 | 22 | {% endif %} 23 | -------------------------------------------------------------------------------- /src/_includes/partials/components/pagination.njk: -------------------------------------------------------------------------------- 1 | {% set paginationLinkTokens = 'leading-tight font-sans text-500 weight-mid box-inline-flex align-center' %} 2 | 3 | {% if paginationNextUrl or paginationPrevUrl %} 4 |
5 |
6 | 22 |
23 | {% endif %} 24 | -------------------------------------------------------------------------------- /src/_includes/partials/components/post-list.njk: -------------------------------------------------------------------------------- 1 | {% if postListItems.length %} 2 |
3 |
4 |

{{ postListHeading }}

5 |
    6 | {% for item in postListItems %} 7 |
  1. 8 |

    9 | {{ item.data.title }} 10 |

    11 |

    12 | 13 |

    14 |
  2. 15 | {% endfor %} 16 |
17 |
18 |
19 | {% endif %} 20 | -------------------------------------------------------------------------------- /src/_includes/partials/global/availability.njk: -------------------------------------------------------------------------------- 1 |
2 |

Hi 👋, I’m Andy — an educator and web designer

3 |
{{ site.availability | markdownFilter | safe }}
4 |

5 | Sign up for updates 6 |

7 |
8 | -------------------------------------------------------------------------------- /src/_includes/partials/global/meta-info.njk: -------------------------------------------------------------------------------- 1 | {% set pageTitle = title + ' - ' + site.name %} 2 | {% set pageDesc = '' %} 3 | {% set siteTitle = site.name %} 4 | {% set currentUrl = site.url + page.url %} 5 | 6 | {% if metaTitle %} 7 | {% set pageTitle = metaTitle %} 8 | {% endif %} 9 | 10 | {% if metaDesc %} 11 | {% set pageDesc = metaDesc %} 12 | {% endif %} 13 | 14 | {{ pageTitle }} 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {% if site.authorHandle %} 23 | 24 | {% endif %} 25 | 26 | {% if metaDesc %} 27 | 28 | 29 | 30 | {% endif %} 31 | 32 | {% if socialImage %} 33 | 34 | 35 | 36 | 37 | {% endif %} 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/_includes/partials/global/service-worker.js: -------------------------------------------------------------------------------- 1 | const CACHE_KEYS = { 2 | PRE_CACHE: `precache-${VERSION}`, 3 | RUNTIME: `runtime-${VERSION}` 4 | }; 5 | 6 | // URLS that we don’t want to end up in the cache 7 | const EXCLUDED_URLS = [ 8 | 'admin', 9 | '.netlify', 10 | 'https://identity.netlify.com/v1/netlify-identity-widget.js', 11 | 'https://unpkg.com/netlify-cms@^2.9.3/dist/netlify-cms.js' 12 | ]; 13 | 14 | // URLS that we want to be cached when the worker is installed 15 | const PRE_CACHE_URLS = ['/', '/fonts/lora-v13-latin-700.woff2']; 16 | 17 | // You might want to bypass a certain host 18 | const IGNORED_HOSTS = ['localhost', 'unpkg.com', ]; 19 | 20 | /** 21 | * Takes an array of strings and puts them in a named cache store 22 | * 23 | * @param {String} cacheName 24 | * @param {Array} items=[] 25 | */ 26 | const addItemsToCache = function(cacheName, items = []) { 27 | caches.open(cacheName).then(cache => cache.addAll(items)); 28 | }; 29 | 30 | self.addEventListener('install', evt => { 31 | self.skipWaiting(); 32 | 33 | addItemsToCache(CACHE_KEYS.PRE_CACHE, PRE_CACHE_URLS); 34 | }); 35 | 36 | self.addEventListener('activate', evt => { 37 | // Look for any old caches that don't match our set and clear them out 38 | evt.waitUntil( 39 | caches 40 | .keys() 41 | .then(cacheNames => { 42 | return cacheNames.filter(item => !Object.values(CACHE_KEYS).includes(item)); 43 | }) 44 | .then(itemsToDelete => { 45 | return Promise.all( 46 | itemsToDelete.map(item => { 47 | return caches.delete(item); 48 | }) 49 | ); 50 | }) 51 | .then(() => self.clients.claim()) 52 | ); 53 | }); 54 | 55 | self.addEventListener('fetch', evt => { 56 | const {hostname} = new URL(evt.request.url); 57 | 58 | // Check we don't want to ignore this host 59 | if (IGNORED_HOSTS.indexOf(hostname) >= 0) { 60 | return; 61 | } 62 | 63 | // Check we don't want to ignore this URL 64 | if (EXCLUDED_URLS.some(page => evt.request.url.indexOf(page) > -1)) { 65 | return; 66 | } 67 | 68 | evt.respondWith( 69 | caches.match(evt.request).then(cachedResponse => { 70 | // Item found in cache so return 71 | if (cachedResponse) { 72 | return cachedResponse; 73 | } 74 | 75 | // Nothing found so load up the request from the network 76 | return caches.open(CACHE_KEYS.RUNTIME).then(cache => { 77 | return fetch(evt.request) 78 | .then(response => { 79 | // Put the new response in cache and return it 80 | return cache.put(evt.request, response.clone()).then(() => { 81 | return response; 82 | }); 83 | }) 84 | .catch(ex => { 85 | return; 86 | }); 87 | }); 88 | }) 89 | ); 90 | }); 91 | -------------------------------------------------------------------------------- /src/_includes/partials/global/site-foot.njk: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /src/_includes/partials/global/site-head.njk: -------------------------------------------------------------------------------- 1 | 2 | 14 | -------------------------------------------------------------------------------- /src/_includes/partials/global/third-party-comments.njk: -------------------------------------------------------------------------------- 1 | 2 | 8 | -------------------------------------------------------------------------------- /src/_redirects: -------------------------------------------------------------------------------- 1 | /feed /feed/page/0 2 | /links /feed/page/0 3 | /notes /feed/page/0 4 | /hire-me / 302 5 | /pe /wrote/the-power-of-progressive-enhancement/ 6 | /resume / 302 7 | /testimonials / 8 | /tags/writing /writing 9 | /wrote/css-doesn't-suck /wrote/css-doesnt-suck 10 | /wrote/get-a-css-custom-property-value-with-javascript /wrote/get-css-custom-property-value-with-javascript 11 | /wrote/css-specifity-and-the-cascade /wrote/css-specificity-and-the-cascade 301 12 | /wrote/i'm-warming-to-tailwind-css /wrote/im-warming-to-tailwind-css 13 | https://hankchizljaw.netlify.com/* https://hankchizljaw.com/:splat 301! 14 | /feed/page/* https://archive.hankchizljaw.com/feed/page/:splat 301! 15 | /notes/* https://archive.hankchizljaw.com/notes/:splat 301! 16 | /links/* https://archive.hankchizljaw.com/links/:splat 301! 17 | 18 | -------------------------------------------------------------------------------- /src/archive.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Writing Archive' 3 | layout: 'layouts/archive.njk' 4 | permalink: '/writing/index.html' 5 | --- 6 | -------------------------------------------------------------------------------- /src/feed/all.njk: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: feed/all.xml 3 | --- 4 | 5 | 6 | Everything from andy-bell.design 7 | Writing, notes and links from andy-bell.design 8 | 9 | 10 | {{ collections.allFeed | rssLastUpdatedDate }} 11 | https://andy-bell.design 12 | 13 | Andy Bell 14 | me@andy-bell.design 15 | 16 | {%- for post in collections.allFeed.slice(0, 15) %} 17 | {% if not post.data.external %} 18 | {% set absolutePostUrl %}https://andy-bell.design{{ post.url | url }}{% endset %} 19 | 20 | {{ post.data.title }} 21 | 22 | Andy Bell 23 | 24 | 25 | {{ post.date | rssDate }} 26 | {{ absolutePostUrl }} 27 | Link: {{ post.data.linkUrl }}

31 | {% endif %} 32 | {% if post.data.image %} 33 |

34 | {{ alt }} 35 |

36 | {% endif %} 37 | ]]>
38 |
39 | {% endif %} 40 | {%- endfor %} 41 |
42 | -------------------------------------------------------------------------------- /src/feed/writing.njk: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: feed/writing.xml 3 | --- 4 | 5 | 6 | HankChizlJaw Articles 7 | Longform articles written by Andy Bell 8 | 9 | 10 | {{ collections.posts | rssLastUpdatedDate }} 11 | https://andy-bell.design 12 | 13 | Andy Bell 14 | me@andy-bell.design 15 | 16 | {%- for post in collections.posts.slice(0, 15) %} 17 | {% if not post.data.external %} 18 | {% set absolutePostUrl %}https://andy-bell.design{{ post.url | url }}{% endset %} 19 | 20 | {{ post.data.title }} 21 | 22 | Andy Bell 23 | 24 | 25 | {{ post.date | rssDate }} 26 | {{ absolutePostUrl }} 27 | 30 | 31 | {% endif %} 32 | {%- endfor %} 33 | 34 | -------------------------------------------------------------------------------- /src/filters/date-filter.js: -------------------------------------------------------------------------------- 1 | // Stolen from https://stackoverflow.com/a/31615643 2 | const appendSuffix = n => { 3 | var s = ['th', 'st', 'nd', 'rd'], 4 | v = n % 100; 5 | return n + (s[(v - 20) % 10] || s[v] || s[0]); 6 | }; 7 | 8 | module.exports = function dateFilter(value) { 9 | const dateObject = new Date(value); 10 | 11 | const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; 12 | const dayWithSuffix = appendSuffix(dateObject.getDate()); 13 | 14 | return `${dayWithSuffix} ${months[dateObject.getMonth()]} ${dateObject.getFullYear()}`; 15 | }; 16 | -------------------------------------------------------------------------------- /src/filters/likes-filter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Scan webmentions feed for likes only 3 | * 4 | * @param {Array} webmentions 5 | * @param {String} url 6 | * @returns {Array} 7 | */ 8 | const likesFilter = function(webmentions, url) { 9 | const allowedTypes = ['like-of']; 10 | const hasRequiredFields = entry => { 11 | const {author} = entry; 12 | return author.name && author.photo && author.url; 13 | }; 14 | 15 | return webmentions 16 | .filter(entry => entry['wm-target'] === url) 17 | .filter(entry => allowedTypes.includes(entry['wm-property'])) 18 | .filter(hasRequiredFields); 19 | }; 20 | 21 | module.exports = likesFilter; 22 | -------------------------------------------------------------------------------- /src/filters/markdown-filter.js: -------------------------------------------------------------------------------- 1 | const markdownIt = require('markdown-it')({ 2 | html: true, 3 | breaks: true, 4 | linkify: true 5 | }); 6 | 7 | module.exports = function markdown(value) { 8 | return markdownIt.render(value); 9 | }; 10 | -------------------------------------------------------------------------------- /src/filters/w3-date-filter.js: -------------------------------------------------------------------------------- 1 | module.exports = function w3cDate(value) { 2 | const dateObject = new Date(value); 3 | 4 | return dateObject.toISOString(); 5 | }; 6 | -------------------------------------------------------------------------------- /src/images/demo-image-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andy-set-studio/personal-site-hylia/765710f65eaee9ab6d3bd5170e6a4c61d3e95534/src/images/demo-image-1.jpg -------------------------------------------------------------------------------- /src/images/demo-image-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andy-set-studio/personal-site-hylia/765710f65eaee9ab6d3bd5170e6a4c61d3e95534/src/images/demo-image-2.jpg -------------------------------------------------------------------------------- /src/images/noise.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andy-set-studio/personal-site-hylia/765710f65eaee9ab6d3bd5170e6a4c61d3e95534/src/images/noise.webp -------------------------------------------------------------------------------- /src/images/social-share.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andy-set-studio/personal-site-hylia/765710f65eaee9ab6d3bd5170e6a4c61d3e95534/src/images/social-share.jpg -------------------------------------------------------------------------------- /src/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: 'home' 3 | title: 'Hello, I’m Andy' 4 | postsHeading: 'Latest writing' 5 | archiveButtonText: 'See all writing' 6 | socialImage: 'https://hankchizljaw.imgix.net/social-share.png' 7 | metaTitle: 'Andy Bell - Educator, Web Designer and Web Developer' 8 | metaDesc: 'I’m an educator who focuses on design, front-end development, accessibility and progressive enhancement.' 9 | --- 10 | 11 | I’m an educator who focuses on design, front-end development, accessibility and progressive enhancement. I teach at [Piccalilli](https://piccalil.li/) and [Front-End Challenges Club](https://front-end-challenges.club/). 12 | 13 | I’m also available for short, freelance front-end development projects. 14 | -------------------------------------------------------------------------------- /src/js/components/theme-toggle.js: -------------------------------------------------------------------------------- 1 | // For syntax highlighting only 2 | const html = String.raw; 3 | 4 | class ThemeToggle extends HTMLElement { 5 | constructor() { 6 | super(); 7 | 8 | this.STORAGE_KEY = 'user-color-scheme'; 9 | this.COLOR_MODE_KEY = '--color-mode'; 10 | } 11 | 12 | connectedCallback() { 13 | this.render(); 14 | } 15 | 16 | getCSSCustomProp(propKey) { 17 | let response = getComputedStyle(document.documentElement).getPropertyValue(propKey); 18 | 19 | // Tidy up the string if there’s something to work with 20 | if (response.length) { 21 | response = response.replace(/\'|"/g, '').trim(); 22 | } 23 | 24 | // Return the string response by default 25 | return response; 26 | } 27 | 28 | applySetting(passedSetting) { 29 | let currentSetting = passedSetting || localStorage.getItem(this.STORAGE_KEY); 30 | 31 | if (currentSetting) { 32 | document.documentElement.setAttribute('data-user-color-scheme', currentSetting); 33 | this.setButtonLabelAndStatus(currentSetting); 34 | } else { 35 | this.setButtonLabelAndStatus(this.getCSSCustomProp(this.COLOR_MODE_KEY)); 36 | } 37 | } 38 | 39 | toggleSetting() { 40 | let currentSetting = localStorage.getItem(this.STORAGE_KEY); 41 | 42 | switch (currentSetting) { 43 | case null: 44 | currentSetting = 45 | this.getCSSCustomProp(this.COLOR_MODE_KEY) === 'dark' ? 'light' : 'dark'; 46 | break; 47 | case 'light': 48 | currentSetting = 'dark'; 49 | break; 50 | case 'dark': 51 | currentSetting = 'light'; 52 | break; 53 | } 54 | 55 | localStorage.setItem(this.STORAGE_KEY, currentSetting); 56 | 57 | return currentSetting; 58 | } 59 | 60 | setButtonLabelAndStatus(currentSetting) { 61 | this.modeToggleButton.innerText = `${ 62 | currentSetting === 'dark' ? 'Light' : 'Dark' 63 | } theme`; 64 | this.modeStatusElement.innerText = `Color mode is now "${currentSetting}"`; 65 | } 66 | 67 | render() { 68 | this.innerHTML = html` 69 |
70 |
71 | 74 |
75 | `; 76 | 77 | this.afterRender(); 78 | } 79 | 80 | afterRender() { 81 | this.modeToggleButton = document.querySelector('.js-mode-toggle'); 82 | this.modeStatusElement = document.querySelector('.js-mode-status'); 83 | 84 | this.modeToggleButton.addEventListener('click', evt => { 85 | evt.preventDefault(); 86 | 87 | this.applySetting(this.toggleSetting()); 88 | }); 89 | 90 | this.applySetting(); 91 | } 92 | } 93 | 94 | if ('customElements' in window) { 95 | customElements.define('theme-toggle', ThemeToggle); 96 | } 97 | 98 | export default ThemeToggle; 99 | -------------------------------------------------------------------------------- /src/pages/404.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: '404 - not found' 3 | permalink: '/404.html' 4 | --- 5 | 6 | Oh shoot, that page can’t be found! This site is currently under construction, so if this a link that you know _used_ to exist, please let me know by [getting in touch](mailto:me@andy-bell.design) at me@andy-bell.design. 7 | 8 | Anyway, here’s a picture of my cats when they were kittens. 9 | 10 | ![A black cat and a tabby cat as kittens](https://hankchizljaw.imgix.net/kittens.jpg 'Delilah (left) and Dora (right) when they were cute kittens. Now they are very not cute chonkers') 11 | -------------------------------------------------------------------------------- /src/pages/now.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: 'layouts/page.njk' 3 | title: 'What I am doing now' 4 | permalink: /now/index.html 5 | --- 6 | 7 | **Last updated**: 23, June 2020 8 | 9 | It’s still an extremely uncertain time at the moment with the Coronavirus pandemic. It’s got me working back from home again which is bittersweet. Bitter that the pandemic forced my hand, but sweet that I’m back working where I am happiest. 10 | 11 | I recently hired desk space and a little office and wasn’t very happy at either, so it is working out better for me. 12 | 13 | I am continuing to focus my attention on: 14 | 15 | - [Piccalilli](https://piccalil.li/) - A mix of free and premium tutorials, articles and courses about web design and front-end development. It also has a regular newsletter. 16 | - [Learn Eleventy From Scratch Course](https://piccalil.li/course/learn-eleventy-from-scratch/) - The debut course for Piccalilli which has just launched. It’s a linear project-based course where you build a [stunning website](//issue33.com) to learn Eleventy. 17 | - [CUBE CSS](https://piccalil.li/blog/cube-css/) - A CSS methodology that I use to build out front-ends of _all_ sizes. I’m making more material such as workshops, talks and documentation. 18 | 19 | I’m also keeping the book I co-authored, [Every Layout](https://every-layout.dev/) up to date. 20 | -------------------------------------------------------------------------------- /src/pages/pages.json: -------------------------------------------------------------------------------- 1 | { 2 | "layout": "layouts/page.njk" 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/uses.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'HankChizlJaw Uses' 3 | permalink: '/uses/index.html' 4 | --- 5 | 6 | Here’s all the stuff I use day-to-day to do my job. 7 | 8 | ## Code setup 9 | 10 | I use [Vim](https://www.vim.org/) for coding and I use it within [iTerm2](https://iterm2.com/). You can [check out my Vim setup](https://github.com/hankchizljaw/chizlvim) over on GitHub. 11 | 12 | For my editor and terminal font, I use [Operator Mono](https://www.typography.com/fonts/operator/styles). I created a version of [Panda Syntax](https://github.com/PandaTheme) for my theme. 13 | 14 | I use [VS Code](https://code.visualstudio.com/) and/or [Sublime Merge](https://www.sublimemerge.com/) if things get a bit hairy. I also like to have a go with [Sublime Text](https://www.sublimetext.com/) sometimes, for old times sake. 15 | 16 | I use [IA Writer](https://ia.net/writer) for writing nearly all content. It’s a super good, focused writing tool. 17 | 18 | ## Computer stuff 19 | 20 | I have a [2019 16” MacBook Pro](https://www.apple.com/uk/macbook-pro-16/) with an 8 core i9 processor, 1TB SSD drive and 32GB of RAM. It’s by far the best Mac I’ve ever had and I’ve been a Mac user since the original weird coloured iMacs! 21 | 22 | I’ve got an [LG 27UD88-W](https://www.lg.com/uk/monitors/lg-27UD88-W), 4K, 27” USB-C screen that does the job nicely. All I do is plug the ol’ MacBook in. I prefer Apple Cinema Displays but they’re long gone now. 23 | 24 | I have a [RØDE Podcaster](http://www.rode.com/microphones/podcaster) for recording my voice. 25 | 26 | ## Organisation 27 | 28 | I live in notebooks and my life revolves around my Bullet Journal, which is a [LEUCHTTURM1917 Bullet Journal](https://www.leuchtturm1917.co.uk/bullet-journal.html?gclid=EAIaIQobChMIle7q9OCU5wIVCFXTCh26LAFjEAAYAiAAEgLSjvD_BwE). I also have piles and piles of [Field Notes](https://fieldnotesbrand.com/) which I use for sketches, lists and general note taking. 29 | 30 | [Notion](https://www.notion.so/) is the beating heart for everything else. I plan content, run projects and run all client work through there. 31 | 32 | I also use [Loom](https://loom.com) _loads_ for screencasting ([especially for clients](/wrote/screencasting-the-secret-sauce-for-less-time-wasted-in-meetings/)). 33 | 34 | ## This website 35 | 36 | This website is a customised version of my [Eleventy Theme, Hylia](https://hylia.website). You can [see the source code here](https://github.com/hankchizljaw/personal-site-hylia). 37 | 38 | Tech it uses: 39 | 40 | - [Eleventy](https://11ty.dev) 41 | - Sass 42 | - Design Tokens 43 | - Web Components 44 | 45 | ## Office 46 | 47 | I like plants and have a ~Bonsai Tree, some succulents and a dangly boi~ which I have no idea what it’s called. (all the plants died 😭) 48 | 49 | It’d be rude not to show y’all the office, so here it is! 50 | 51 | ![My office desk with all of the above described items](https://photos.hankchizljaw.com/photos/large/I%E2%80%99ve%20had%20a%20bit%20of%20a%20move%20around-686656.jpeg) 52 | -------------------------------------------------------------------------------- /src/posts/a-dive-into-freelance-life.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: 'layouts/post.njk' 3 | title: 'A dive into freelance life' 4 | date: '2018-11-05' 5 | tags: 'writing' 6 | --- 7 | 8 | After a rather lengthy, draining search for a new role, I’ve decided to give freelance design and development a go. I feel like this could be the best way for me to work on things that genuinely make a positive impact on people, while also maintaining work-life balance for my young family. 9 | 10 | ## Making work, work for me 11 | 12 | The gang and I at [No Divide](https://nodividestudio.com/) are going on hiatus and I want to maintain the flexible work day that we had there. We very much made work fit around us, rather than the other way around and I was struggling to get a role in a company that worked like that. 13 | 14 | I also want to maintain a sensible pace of work. We put a lot of consideration into what we did there and I’ve struggled to find companies that would be an exact fit on that front. It’s not to say we necessarily did things _better_, but it’s my preferred way of working, rather than shipping fast and frequently. 15 | 16 | After a couple of ideal opportunities got away from me, I decided to go for the next best thing: independence. I actually started my career in the web as a freelancer, back in 2009, so it’ll be interesting to do it again with _much_ more experience and _much_ better contacts. I’ve learnt how _not_ to do things in various agency roles too, so I feel pretty confident that I’ll do it right this time. 17 | 18 | ## The sort of work I’m looking for 19 | 20 | I’m really into progressive enhancement, user experience, accessibility and inclusivity. I’m multi-disciplined in that I work as both a designer and a developer. This means I can work from concept, right though to delivery if needed. Any projects that allow me to do that will be warmly received. 21 | 22 | These are the sort of projects / contexts that I’d be most useful in: 23 | 24 | - A new startup that needs help making an MVP or prototype 25 | - Design systems 26 | - Pattern libraries (design and/or build) 27 | - User experience research, design and prototypes 28 | - User interface design 29 | - HTML and CSS development 30 | - React, Vue and Node development 31 | - Accessibility audits and improvements 32 | - WordPress / Craft CMS / Perch CMS websites (design and/or build) 33 | - Jekyll / Hugo / Gatsby / Eleventy websites (design and/or build) 34 | - Development for agencies that need a bit of help to get projects done 35 | 36 | ## Hire me 37 | 38 | I’m booking projects for 2019, so I’d love to talk. I prefer to work remotely, but I’m very happy to come to you for meetings and workshops. 39 | 40 | Get in touch 41 | -------------------------------------------------------------------------------- /src/posts/a-double-launch-day.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: 'layouts/post.njk' 3 | title: 'A double launch day' 4 | date: '2018-08-23' 5 | tags: 'writing' 6 | --- 7 | 8 | I had planned to write an _"introducing"_ post for my new website to launch it with, but this morning I had an idea; What if I collected links to personal sites so we could all subscribe to each other's feeds and get inspired by each other's work? I was going to make it a Github readme, but thought why not make it a simple website instead? 9 | 10 | I'd be silly to launch a project like that into the wild without my brand new website, right? Of course I would, so I pushed the button and launched it! 11 | 12 | Thanks to [Zach Leatherman](https://www.zachleat.com)'s fantastic [Eleventy static site generator](https://11ty.io) and modern CSS, I was able to put [personalsit.es](https://personalsit.es) together while I waited for my coffee to brew. I had so much faith in CSS grid, that I didn't even test it with dummy content. 13 | 14 | I even gave Zach a half-arsed testimonial on twitter: 15 | 16 | > It's because of your site builder being such a dream to work with! It literally took 20-30mins to get it out and shipped. It took longer for DNS to propagate. 17 | > 18 | > Yours, 19 | > Hank 20 | > 21 | > [@hankchizljaw - 23/08/2018](https://twitter.com/hankchizljaw/status/1032614914140057600) 22 | 23 | Maybe that can replace [my current half-arsed one on the website](https://www.11ty.io/#happy-developers). 24 | 25 | --- 26 | 27 | I like this modern web where we can still put websites together quickly, but now we know they're going to be much more resilient, thanks to the hard work of the community to bring us CSS Grid et al. 28 | 29 | Head over to [personalsit.es](https://personalsit.es) and see all of the fantastic work that's already been added. Maybe even go over to the [GitHub repository](https://github.com/hankchizljaw/personalsit.es) and add your own personal site? 30 | -------------------------------------------------------------------------------- /src/posts/a-minimal-react-base-project.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'A minimal React base project' 3 | date: '2020-01-07' 4 | tags: ['writing'] 5 | --- 6 | 7 | I started building out the [latest Front-End Challenges Club challenge](https://front-end-challenges.club/challenge-004-progress-button) this morning and because I’d [promised to build it in React](https://twitter.com/hankchizljaw/status/1213587540772630531), I started tinkering around with webpack to get my basic setup going. When I looked in the output directory, though, I gasped. 8 | 9 | The _production bundle_ was around 130kb (no gzip) and all I had was **one React component**. It was clear, thanks to [VS Code Import Cost](https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost) that the issue was React DOM, so I changed `import ReactDOM from 'react-dom';` to `import {render} from 'react-dom';`, thinking there’d be some tree-shaking, but nope: still a massive bundle. 10 | 11 | I spent way to long messing around with babel, webpack, npm scripts and the works to shave off the kbs and nothing was working. I was ready to give up at this point because I just don’t like shipping lots of JavaScript, but I did make a promise in my [end of year review](https://hankchizljaw.com/wrote/2019:-a-year-in-review/#heading-time-to-embrace-react-and-gatsby) that I would get more involved in React and [Gatsby](https://www.gatsbyjs.org/) this year, so I stuck at it. 12 | 13 | ## Preact, the saviour 14 | 15 | I could have messed around all day trying to shrink the bundle, but frankly, that sort of stuff is not my strongest skill. I remembered why I suddenly changed my attitude towards Gatsby, though. It was because of this package: [gatsby-plugin-preact](https://www.gatsbyjs.org/packages/gatsby-plugin-preact/). 16 | 17 | I use that for [Front-End-Challenges Club](https://front-end-challenges.club/) and when it builds, the output JavaScript bundle is _tiny_ compared to when it was React, so I thought I’d play with the same approach. Luckily, the Preact team are smart as heck and created [preact-compat](https://preactjs.com/guide/v8/switching-to-preact/#how-to-alias-preact-compat). Thanks to this, I made no code changes and _just_ changed my webpack config. This brought the production bundle down from ~130kb to ~19kb!! Hell yeh. 18 | 19 | ## I made it into a thing 20 | 21 | Of course, me being me, I made it into a repo, which you [can grab here](https://github.com/hankchizljaw/minimal-react-base). It’s a little base project that lets you sling a bit of React (Preact) on a web page, with no bells and whistles. 22 | 23 | I’d love it if some folks from the React community or Preact community could cast their eyes on it because it would be good to make sure all is as optimised as possible. I’m a bit out of date with React knowledge and keen to get better. 24 | 25 | ## Wrapping up 26 | 27 | The morale of the story: instead of wasting hours tinkering, think outside of the box and bit and get amazing results. 28 | 29 | I’ll be using [this little base project](https://github.com/hankchizljaw/minimal-react-base) for all sorts of exciting stuff, including some React stuff for Every Layout in the future. 30 | 31 | Stay tuned! 32 | -------------------------------------------------------------------------------- /src/posts/a-short-note-on-my-social-activity.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: 'layouts/post.njk' 3 | title: 'A short note on my social activity' 4 | date: '2018-08-14' 5 | updated: '2018-08-20' 6 | tags: 'writing' 7 | external: false 8 | --- 9 | I'm going to step away from Twitter for a while. I'll still post stuff—especially when I'm up to something I think y'all might find interesting, but the platform is just too toxic to spend any amount of time on. I've recently done the same with Facebook and have certainly benefitted from that decision. 10 | 11 | You can find me over at Mastodon. Here's a [link to my profile](https://toots.hankchizljaw.io/@hankchizljaw). I'll be a lot more active over there and I hope to see you folks around there too. 12 | 13 | I'm also going to re-work this site and get on the RSS train. I love [Zach Leatherman's](https://mastodon.social/@zachleat) [11ty generator](http://11ty.io) and I'll use that. I just wish there was something out there that would make posting short notes with markdown easier—especially from my phone. Maybe something will crop up. 14 | 15 | Anyway, I hope to see you all soon on [Mastodon](https://mastodon.social/@hankchizljaw) or wherever. I'll still check my Twitter DMs, but you're probably better off dropping me an [email](mailto:andy@hankchizljaw.io). 16 | 17 | Take care of yourselves. 18 | -------------------------------------------------------------------------------- /src/posts/adding-webmentions-to-my-personal-site.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Adding Webmentions to my personal site' 3 | date: '2019-07-05' 4 | tags: ['indieweb', 'webmentions'] 5 | --- 6 | 7 | _**Update 21 October 2019**: I decided to remove these from my site as monitoring and moderating them became a lot of work on popular articles. I think the tech is cool as heck, it’s just not for me at this time._ 8 | 9 | _I might pick it up again in the future without the Twitter integrations. Hopefully this article will be useful for you, regardless of my choice!_ 10 | 11 | --- 12 | 13 | I think Webmentions are cool because they are a proper web standard, so I’ve been keen to get them going on my site—especially after I [burned it to the ground](https://andy-bell.design/wrote/eating-my-own-dog-food/) and replaced it with an instance of [Hylia](https://hylia.website). 14 | 15 | I’ve had [Max’s tutorial](https://mxb.dev/blog/using-webmentions-on-static-sites/) bookmarked for _ages_ and it is a fantastic resource. I basically followed it to the letter, with a couple of modifications to match the code style of this site. If you’re looking to implement yourself, definitely head over to his site. 16 | 17 | I did do the implementation with a [PR](https://github.com/andybelldesign/personal-site-hylia/pull/2), though, so if you want to see the code that I implemented, check it out. 18 | 19 | ## A basic implementation 20 | 21 | This implementation that I’ve added is _rough as hell_, but it’s a good starting point. I really want to improve the rebuild setup and also post Webmentions from my site as replies to other folks blogs. I’ll certainly be using [Remy’s project](https://webmention.app) for that. 22 | 23 | For the builds, I have [IFTTT](http://ifttt.com) running a [Netlify](https://www.netlify.com) hook every hour, on the hour. It’ll do for now, but if anyone has any tricks for a slicker process, let me know. 24 | 25 | I’ve also got [Bridgy](https://brid.gy) on the go to pull in likes and replies from Twitter. That service was very easy to implement. Very handy indeed. 26 | 27 | ## User experience and barrier to entry 28 | 29 | Something that I can't stop thinking about with Webmentions is that the barrier to entry is **very** high. I’ve been making websites for a long time and it certainly had me sweating. I’m also a very confident developer who grinds through being stuck, so I do worry about how easy it would be for folks to drop-off and implement a nasty comments system like Disqus when they get stuck, if they’re not super confident. 30 | 31 | I think the user experience for users manually posting their Webmentions to websites is pretty shit too, but as it stands, we don’t really have any other options other than a crappy little `` at the end of our pages. 32 | 33 | With both of those issues, I would love it if a group of us got our heads together and worked out ways to both reduce the barrier to entry and consider the user experience of posting Webmentions. Jump into my Webmentions if that sounds like something you’d like to do. 34 | 35 | One thing you could do to help is blog about implementing Webmentions on your site. If it wasn’t for [Max](https://twitter.com/mxbck), I probably wouldn’t have bothered, so your input about your context will be valuable. I can promise that. 36 | 37 | ## Wrapping up 38 | 39 | This has been fun and it certainly kept me entertained on the long [journey to Brighton](https://andy-bell.design/notes/198). 40 | 41 | I imagine folks are wondering if I’m going to implement this stuff on [Hylia](https://hylia.website). I’m not going to do that quite yet because my implementation is rough, so I think it’d cause people more headaches. Maybe after some more thought and work, I can write some helpful docs or a very specific Hylia tutorial. 42 | 43 | Anyway, you can now comment on any post, note or link! 44 | -------------------------------------------------------------------------------- /src/posts/break-out-of-the-echo-chamber.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Break out of the echo chamber' 3 | layout: 'layouts/post.njk' 4 | date: '2019-04-06' 5 | tags: 'writing' 6 | --- 7 | 8 | As much value as Twitter can bring to the web community, in terms of discussion: one thing it’s terrible for is statements, like this: 9 | 10 | > “...Web Components have failed” 11 | > 12 | > [Kyle Mathews - 05/04/2019](https://twitter.com/kylemathews/status/1114288205367525377?s=21) 13 | 14 | This particular example of absolutistism is more than likely to be framed as a comparison to React, by proxy of the author’s apparent interests. That statement in the context of the wider web is fundamentally wrong, though. But, it’s also an understandable opinion to hold based on the common attitude of what I presume is his echo chamber is: the React community. 15 | 16 | I’m also probably in an echo chamber—the so-called “Old Guard”—so I also often find myself making absolutist statements of the opposite opinion. I am working hard to get out of my echo chamber, though by trying to follow more moderate members of the React community to try to see things from their perspective, rather than my own pessimistic perspective. 17 | 18 | We should try hard to break out of our echo chambers because if not, it’s easy to forget and discount the wider community, which often results in less desirable outcomes. Brexit is now a classic example of this, because in my echo chamber, we were all flabbergasted at the result, because we all generally float around the left of the political spectrum. We’re all mostly middle class, too. A serious lesson was learned about how people who were systemically screwed over by successive governments were presented with genuine change and understandably snatched at it. Yet, people in my echo chamber have the audacity to call these voters “stupid”, which is incredibly unfair and short-sighted. 19 | 20 | --- 21 | 22 | So much of my echo chamber is consumed by people, including myself, who have a very dim view of JavaScript frameworks being thrown at everything, arguing with the people who are in the process of throwing JavaScript frameworks at everything. We forget one very important thing, though: we represent the _minority_ of the web community and our arguments probably look very pointless and silly to the _majority_. 23 | 24 | The _majority_ of the web community are probably building—y’know—modest websites. There’s a reason why [WordPress powers 33.5% of the web](https://w3techs.com/technologies/details/cm-wordpress/all/all): because most of the web isn’t big applications or design systems—it’s straight-up websites. We would all do well to remember that. 25 | 26 | To tie all this back to Web Components and React: saying “Web Components have failed” in the context of a certain, _minority_ echo chamber might well be true. Web Components are, however, probably going to be more useful to the _majority_ of the web community, who’d benefit from an encapsulated, low-level primitive to enhance their modest website. Let’s also not forget that they are currenly very useful to folks building all sorts of exciting things. 27 | 28 | We should try harder as a whole web community to break out of our echo chambers, and appreciate other’s, conflicting views more. This includes me, especially. 29 | -------------------------------------------------------------------------------- /src/posts/browser-diversity.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: 'layouts/post.njk' 3 | title: 'Browser diversity' 4 | date: '2018-12-08' 5 | tags: 'writing' 6 | --- 7 | 8 | With [Edge officially scrapping its own engine in favour of Chromium](https://github.com/MicrosoftEdge/MSEdge/blob/master/README.md), the community has had a mixed response. That comes as no surprise as we tend to be polarised with these sort of things. 9 | 10 | I have found some of the takes fascinating. These takes have been both reasonably positive and understandably wary. I am very much in the wary camp—even when there are [clear positive opportunities](https://motherboard.vice.com/en_us/article/59vke8/microsoft-putting-edge-on-chromium-will-fundamentally-change-the-web), in the eyes of an optimist. 11 | 12 | The main reason I am wary is that [I have a lot of mistrust of Microsoft and Google](/wrote/my-fractured-relationship-with-big-tech/) even before this situation, so I’m naturally not going to embrace them being in close cahoots with each other. Not just this relationship, though because as [others have articulated better than I ever can](http://www.zeldman.com/2018/12/07/browser-diversity-starts-with-us/), there’s a huge worry about a lack of diversity with web browsers. Some will say “this is great because we can concentrate on other stuff, rather than browser bugs”, whereas [others see the dangers of browser engine monopolisation](https://daverupert.com/2018/12/edge-goes-chromium/). I do too and can’t believe some aren’t panicking after the Internet Explorer monopoly and subsequent support nightmare are only reasonably recent history. 13 | 14 | [Eric Meyer](https://meyerweb.com/) sums up how I feel: 15 | > “Those who do not learn history are doomed to repeat it. Those who do learn history are doomed to watch it repeated by those who didn’t.” 16 | 17 | [Tweet from January 2017](https://twitter.com/meyerweb/status/825136361682374656) 18 | 19 | It’s a quote that’s not directly related to this situation but I do feel like a bit of reading up on the Internet Explorer monopoly alone should have even the most short sighted amongst us worried. 20 | 21 | I also think that we’re in another cycle of “just build X into the browser” with [React being the most recent framework of choice](https://twitter.com/mjackson/status/1050600496992374785). Why not have a browser monopoly cycle to go with it? Again, the same quote from above applies. 22 | 23 | ## Uh oh, AMP 24 | 25 | I often jokingly link AMP to the era when almost every consumer accessed the internet with those pesky AOL CDs. Note how I say “internet” and not “the web”, because that proprietary, corporate land-grab couldn’t be further from the principles of the web. 26 | 27 | The thing is, with a key competitor to Google now giving up and embracing Chromium, what’s to say they won’t also embrace Google’s other pet open source project, [AMP](https://www.ampproject.org/)? What if they also give up on Bing? It all starts getting a bit “AOL” at that point. 28 | 29 | ## Support Firefox 30 | 31 | I’ll say it bluntly: we must support [Firefox](https://www.mozilla.org/en-GB/firefox/new/). We can’t, as a community allow this browser engine monopoly. We must use Firefox as our main dev browsers; we must encourage our friends and families to use it, too. 32 | 33 | Yes, it’s not perfect, nor are Mozilla, but we can help them to develop and grow by using Firefox and reporting issues that we find. If we just use and build for Chromium, which is looking likely (*cough* Internet Explorer monopoly *cough*), then Firefox will fall away and we will then have just one major engine left. I don’t ever want to see that. 34 | 35 | [I’ve tried to use Firefox](/notes/18/) before and [given up way too easily](/notes/22/), so I pledge now to use it as my main dev browser and talk positively about it as much as I can on here and Twitter. 36 | 37 | Will you join me? I hope you will, because we _can_ make a positive impact on this situation as a community. 38 | -------------------------------------------------------------------------------- /src/posts/bypass-service-worker-on-localhost.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: 'layouts/post.njk' 3 | title: 'Bypass service worker on localhost' 4 | date: '2018-09-04' 5 | tags: 'writing' 6 | --- 7 | 8 | The main thing that's been holding me back from using Firefox full-time is that there appears to be no way to _bypass_ a service worker. I often use the provided “bypass service worker” option in Chrome dev tools to do this. 9 | 10 | I've been hunting around and [pleading](https://toots.hankchizljaw.io/@hankchizljaw/100666743737034549) with the web to no avail, so I put the responsibility onto myself. 11 | 12 | For now, I _just_ want to ignore `localhost` urls, but the below snippet could allow you to prevent the service worker's `fetch` event from continuing for any `hostname` that you assign in `ignoredHosts`. 13 | 14 | ```javascript 15 | self.addEventListener('fetch', evt => { 16 | 17 | // Define the hostnames that you want to ignore 18 | const ignoredHosts = ['localhost']; 19 | 20 | // Destructure the hostname out of the event's request 21 | // URL by creating a new URL instance 22 | const {hostname} = new URL(evt.request.url); 23 | 24 | // Bail out if our definition contains this url 25 | if (ignoredHosts.indexOf(hostname) >= 0) { 26 | return; 27 | } 28 | 29 | }); 30 | ``` 31 | 32 | Go ahead and [grab the code from this Gist](https://gist.github.com/hankchizljaw/ab90fadcf33de890f303ea483cd83515). 33 | 34 | ## Let's break it down 35 | 36 | 1. You define your host in the `ignoredHosts` array 37 | 2. We construct a new [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) because we can't access the window in a service worker. This URL gives us access to the magic `hostname` property which we grab using [destructuring](https://css-tricks.com/learning-gutenberg-4-modern-javascript-syntax/#article-header-id-4) to create the `hostname` constant. 38 | 3. We check the index of the `hostname` within `ignoredHosts`. If it's greater than or equal to 0, we have a match 39 | 4. If it's a match, we bail out 40 | 41 | ## Wrapping up 42 | 43 | A short and sweet tip which I hope you find useful. I can _finally_ use Firefox full-time now which brings me a lot of joy. 44 | 45 | If you can improve the above code, hit me up. It's only a quick fix, so any improvements will be welcomed! 46 | -------------------------------------------------------------------------------- /src/posts/community.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Community' 3 | date: '2020-03-16' 4 | tags: ['writing'] 5 | --- 6 | 7 | People are suddenly working from home and I know from experience how difficult it can be. I personally prefer the separation, but I know that others really struggle with it, so I’ve created two communities to try to help. 8 | 9 | ## Piccalilli Community 10 | 11 | **Link**: 12 | **Code of Conduct**: 13 | 14 | I was planning to set up the Piccalilli Community in a couple of months time as the site started getting more content and the courses started getting published, but I’ve pushed that forward. I think it’ll be nice for people to be able to hang out, chat about front-end development and design in a friendly, safe environment away from the high pressure, unsafe environments that are Twitter and Hacker News. 15 | 16 | It’s already shaping up to be a super friendly place, so you will be more than welcome to come along and join in with us. 17 | 18 | ## Pals Discord 19 | 20 | **Link**: 21 | **Code of Conduct**: 22 | 23 | I also set up a Discord where people can just hang out and be pals. There’s no need to talk about tech stuff—just hang out and enjoy yourselves. I was inspired by [Joel Califa, who also set up something similar](https://twitter.com/notdetails/status/1238956925192151041) (where I’m also a member) and I think it’s a great idea. 24 | 25 | People are going to be spending a lot of time in Slack for work, so I didn’t want to set one of those up. Discord also do Voice (and video) channels which I think will be perfect to satisfy this need: 26 | 27 | > Is it feasible for someone to create a @zoom_us room where people can just pop in throughout the day? 28 | > 29 | > I’m thinking for those who are feeling isolated who want to at least hang out with some folks while they work. 30 | > 31 | > Me and a buddy used to do this on skype years ago and it was nice 32 | 33 | [Original tweet](https://twitter.com/hankchizljaw/status/1238764430764183554) 34 | 35 | I like how people will be able to drop in and hang out whenever they like. Again, this place is already shaping up to be nice as heck. 36 | 37 | The Code of Conduct of Conduct is the same as the Piccalilli one for now. 38 | 39 | ## Wrapping up 40 | 41 | I’m fully aware of my own privilege so please, if you think I’m missing something in terms of keeping people safe, please let me know. 42 | 43 | I really hope these little communities will help _you_ if you are struggling a bit. We’re in it for the long-haul with COVID-19, so little things like this can go a long way. 44 | 45 | Take it easy and wash your hands 🖤 -------------------------------------------------------------------------------- /src/posts/competing-by-mimicking.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: 'layouts/post.njk' 3 | title: 'Competing by mimicking' 4 | date: '2019-03-20' 5 | tags: 'writing' 6 | --- 7 | 8 | I see this sort of phrase dotted around social media every now and then: 9 | 10 | > “We do X like this, because we’re trying to compete with native apps” 11 | 12 | I always find it a bit confusing because oftentimes, “X” is a complicated solution to an already existing, acceptable platform technology, like...um...using CSS-in-JS over CSS, for instance. I also find the phrase confusing because “X” can sometimes mimic the way native app developers do something in a native app programming environment, like Swift or Java, with storyboards and views. 13 | 14 | The thing I find the most confusing, though is the concept of competing by _mimicking_. In any other context, it’d be laughable that someone would _compete_ by _mimicking_ someone else. 15 | 16 | The whole idea of competing with native apps seems pretty daft to me, too. The web gives us so much for free that app developers could only _dream_ of, like URLs and the ability to publish to the _entire world_ for free, immediately. 17 | 18 | In my mind, the only way to “compete” with native apps is to _do better than native apps_—and with the web platform consistently improving and enabling us to produce app-like experiences, with Service Workers, ES6+ JavaScript, modern CSS and Web Components: we are very much on the path to _do better than native apps_. This includes the handy Progressive Web App capabilities that we have available to us, too. 19 | 20 | As [Jeremy Keith says](https://adactio.com/journal/12461): 21 | 22 | > “If the goal of the web is just to compete with native, then we’ve set the bar way too low.” 23 | 24 | I believe in the web and will continue to believe that building Progressive Web Apps that _embrace_ the web platform will be far superior to the non-inclusive walled garden that is native apps and their app stores. I just wish that others thought like that, too. -------------------------------------------------------------------------------- /src/posts/contraction-logger.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: 'layouts/post.njk' 3 | title: 'Contraction logger' 4 | date: '2018-09-11' 5 | tags: 'writing' 6 | --- 7 | 8 | My partner is due to give birth at any moment, so I did what any other sensible person does in this situation and created myself a contraction logging, progressive web app. 9 | 10 | For those of you that haven't gone through the labour process, it's a good idea to keep track of how long it's been between each contraction in the early parts. It's also pretty darn handy to keep track of how long each contraction went on for. 11 | 12 | In the U.K. you're supposed to stay at home until you're having roughly 3 contractions within 10 minutes. That makes sense because early labour can go on for _hours—even days_, so they don't want you clogging up their wards. 13 | 14 | ## How it works 15 | 16 | You go to [here](https://99ab819b7d18458aa344328db589274e.codepen.website) and you get sent to a [CodePen](https://codepen.io) project. All this does is present you with a [Boilerform](https://boilerform.design) powered form. This is where you log a duration as an entry. As you log each entry, it keeps a track of the timestamps and shows you a when each contraction was and how long it was between each one. It does all of this by storing a JSON object in local storage. 17 | 18 | It's a very simple and _highly janky_ app that I hope someone can find a use of. You can add it to your home screen on iOS too. You can also [see the source code on CodePen](https://codepen.io/hankchizljaw/project/editor/XbpWqE) if you want to have a poke around. 19 | 20 | Here's a quick screencast of me landing on the app, adding it to the home screen and then making a couple of entries on an iOS simulator. 21 | 22 | 23 | 24 | ## Wrapping up 25 | 26 | If you find a use for this, let me know. I'd love to hear from you! Regardless, it was a fun little thing to make and give to [Boilerform](https://boilerform.design) another spin, which I rarely get to do since I made it. 27 | 28 | I'll also finish this article by saying it's ok to make little things like this. Sure, it's got loads of dead code and is probably riddled with bugs and it's also a bit of a UX nightmare, but that doesn't matter. It serves the purpose of helping me and my partner through a stressful period, which is the most important and only role it plays. 29 | 30 | I strongly recommend making stuff like this when the pressure is off. As an industry, our work has gotten so damn complicated, so it's good to just keep things simple and muck around every now and then. Trust me, it's good for the soul 🙂 31 | -------------------------------------------------------------------------------- /src/posts/creating-a-full-bleed-css-utility.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: 'layouts/post.njk' 3 | title: 'Creating a full bleed CSS utility ' 4 | subTitle: 'Piccalilli CSS Utility — Issue #2' 5 | issueSlug: '/issues/2' 6 | date: '2019-04-05' 7 | tags: ['writing', 'piccalilli-pattern'] 8 | hasCodePen: true 9 | --- 10 | 11 | Sometimes you want to break your components out of the constraints that they find themselves in. A common situation where this occurs is when you don’t have much control of the container that it exists in, such as a CMS main content area. 12 | 13 | This is even more the case with editing tools such as the [WordPress Gutenberg editor](https://wordpress.org/gutenberg/), where in theory, you could pull in a component from a design system and utilise it in the main content of your web page. In these situations, it can be pretty darn handy to have a little utility that makes the element 100% of the viewport’s width _and_ still maintain its flow within its parent container. 14 | 15 | This is when I normally pull the `.full-bleed` utility class out of my back pocket. 16 | 17 | ## The `.full-bleed` utility 18 | 19 | It’s small, but hella mighty: 20 | 21 | ```css 22 | .full-bleed { 23 | width: 100vw; 24 | margin-left: 50%; 25 | transform: translateX(-50%); 26 | } 27 | ``` 28 | 29 | Here it is in a context where it makes a fancy `