Monica Dinculescu2023-02-27T20:38:44+00:00https://meowni.caMonica DinculescuHallucinating with art models2022-09-01T00:00:00+00:00https://meowni.ca/posts/hallucinations<link type="text/css" rel="stylesheet" href="/css/floatie-bits.css" />
<style>
.floatie-bit img {
object-fit:cover;
transition: transform 0.3s ease-in-out;
cursor: zoom-in}
.floatie-bit img:hover{
transform: scale(1.5);
z-index: 1000;
}
@media (max-width: 700px) {
.floatie-bit > img {
margin-top: 0;
margin-bottom: 24px;
width: 100%;
}
}
</style>
<p>Wow, long time, no posts! Anyway, about them text-to-art generative models going about, eh? Surprising nobody: I am extremely into them. I’ve been using <a href="https://openai.com/dall-e-2/">DALL-E</a> and <a href="https://www.midjourney.com/home/">MidJourney</a> since they came out, and even though tons has been written about them, I wanted to give a slightly different overview: the perspective of someone who isn’t interested that much in their realism skills.</p>
<p>I think that the most compelling place for ML models in an artist’s life is as a tool that specifically <strong>enables</strong>, and doesn’t <strong>replace</strong>, creativity. Machine Learning is amazing at doing something very specific, lots of times, really fast. It’s great at telling me if an image is a cat or a dog. It’s also great at generating one hundred half-dog-half-cats, in different positions, so that I can bypass the dozens of hours I would spend sketching out half-dog-half-cats for a painting that’s <em>actually</em> about the nuclear apocalypse.</p>
<p>I’ve seen a lot of examples of which model is best at painting “The otter with the pearl earring”, but I haven’t seen a lot of comparisons of these models in terms of their potential for creativity– likely because “creativity” is not really quantifiable. I wanted to do this for myself, if anything so that I can figure out how to use my money and credits better, but thought that I might as well put it out there in case anyone else was curious.</p>
<p>This post ended up being looooong, so here’s a Table of Contents:</p>
<ul id="markdown-toc">
<li><a href="#boring-uses-of-interesting-models" id="markdown-toc-boring-uses-of-interesting-models">Boring uses of interesting models</a></li>
<li><a href="#the-barely-scientific-method" id="markdown-toc-the-barely-scientific-method">The (barely scientific) method</a> <ul>
<li><a href="#models" id="markdown-toc-models">Models</a></li>
<li><a href="#cherry-picking-outputs" id="markdown-toc-cherry-picking-outputs">Cherry-picking outputs</a></li>
<li><a href="#what-i-look-for" id="markdown-toc-what-i-look-for">What I look for</a></li>
</ul>
</li>
<li><a href="#results" id="markdown-toc-results">Results</a> <ul>
<li><a href="#1-linocut-print-of-a-girl-bundled-up-in-bed-with-a-stack-of-books-and-a-cat" id="markdown-toc-1-linocut-print-of-a-girl-bundled-up-in-bed-with-a-stack-of-books-and-a-cat">1. “Linocut print of a girl bundled up in bed with a stack of books and a cat”</a></li>
<li><a href="#2-lithograph-of-an-orchid-where-each-flower-has-a-small-skull-inside" id="markdown-toc-2-lithograph-of-an-orchid-where-each-flower-has-a-small-skull-inside">2. “Lithograph of an orchid where each flower has a small skull inside”</a></li>
<li><a href="#3-erik-johansson-photograph-of-a-womansic-hair-that-is-a-literal-bee-hive" id="markdown-toc-3-erik-johansson-photograph-of-a-womansic-hair-that-is-a-literal-bee-hive">3. “Erik Johansson photograph of a woman[sic] hair that is a literal bee hive”</a></li>
<li><a href="#4-a-toucan-wearing-a-60s-apron-sitting-on-a-mid-century-modern-armchair-talking-on-a-rotary-phone-retrofuturism" id="markdown-toc-4-a-toucan-wearing-a-60s-apron-sitting-on-a-mid-century-modern-armchair-talking-on-a-rotary-phone-retrofuturism">4. “A toucan wearing a 60s apron, sitting on a mid century modern armchair, talking on a rotary phone, retrofuturism”</a></li>
</ul>
</li>
<li><a href="#what-have-i-learned" id="markdown-toc-what-have-i-learned">What have I learned?</a></li>
</ul>
<h2 id="boring-uses-of-interesting-models">Boring uses of interesting models</h2>
<p>I use these new models for a very specific thing, and that is as a brainstorming partner/collaborator. This shouldn’t surprise you: I’ve been going off on using ML for co-creation since the days of working on <a href="https://magenta.tensorflow.org/">Magenta</a>, which was the project that forged my views on the topic (shoutout to the smarter-than-me people on the team that let me absorb their well articulated opinions).</p>
<p>I tend to find DALL-E generations like “dogs playing poker” or “Donald Trump but as a cheeseburger” impressive, but kind of boring: yes, the output is funny, but no, I don’t fall asleep thinking about the deep meaning of an orange cheeseburger with a balding head and very small hands. As an artist who is trying to carve a place for myself in the art world (and for these models in my art), I want them to be my co-creators, my partners in crime, but not take over and compromise my style. I want us to brainstorm together, come up with ideas, and then (in most cases), mold this draft into something that I can look at and say “yeah, that looks like something I made”.</p>
<p>This is consistent with how I look at the use of music models like the <a href="https://magenta.github.io/listen-to-transformer/#a1_50693.mid">Music Transformer</a>: absolutely impressive compositions, best suited for an elevator. This isn’t the fault of the model, nor its users, and I am truly not shitting on these outputs (unless they’re for NFTs; always here to shit on NFTs 🙃): I think both background music and memes have a value in society, as do procedural TV shows (they’re filming new Criminal Minds!!!), computer generated or not. But personally, as an artist, I feel fairly emotionally detached from them, as I’m pretty sure their authors do.</p>
<p>The exact same models, when used in a thoughtful and creative way lead to absolutely brilliant things; the ones that make you say “fuck, I wish <strong>I</strong> had thought of that”. Yacht’s <a href="https://www.wired.com/story/how-yacht-used-machine-learning-to-create-their-new-album/?bxid=5cec28c424c17c4c6463a7e9">album</a> made with hard creative rules and machine learning is a wonderful example, as is Karen X. Cheng’s use of DALL-E to imagine the scene <a href="https://twitter.com/karenxcheng/status/1552720889489154048?s=20&t=62SQKCGZANvOlSjOjYMTig">beyond Girl with a Pearl Earring</a>. This is the bit that I care about.</p>
<p>I’ve been trying to post updates about how I personally use these models on my <a href="https://www.instagram.com/meownica.studio/">artstagram</a> (often with a commentary about the process), but as a recent craft fair that just rejected me had to say: I’m quite shit at the socials. Have this blog post instead?</p>
<h2 id="the-barely-scientific-method">The (barely scientific) method</h2>
<h3 id="models">Models</h3>
<p>I used the 4 models I have access to:</p>
<ul>
<li><a href="https://openai.com/dall-e-2/">DALL-E</a>, via the OpenAI website</li>
<li><a href="https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/stable_diffusion.ipynb">Stable Diffusion</a>, via the collab</li>
<li><a href="https://www.midjourney.com/home/">MidJourney</a>, via their Discord bot</li>
<li><a href="https://huggingface.co/spaces/dalle-mini/dalle-mini">DALL-E mini</a>, via the HuggingFace interface.</li>
</ul>
<p>I tried to get a Googler to help me run the prompts on <a href="https://imagen.research.google/">Imagen</a>, but I got no bites, so I have no idea how it fits into this story.</p>
<h3 id="cherry-picking-outputs">Cherry-picking outputs</h3>
<p>I cherry-picked 4 images for each model as follows:</p>
<ul>
<li>DALL-E: Because using it costs real moneydollars these days, I decided to backfit this experiment to prompts I’ve already saved several images for (crucially: this means that I thought the prompt gave interesting enough results for me to care; this is 100% not true of every prompt I try). I also ran each prompt one more time to generate 4 more outputs, and then picked the best 3 out of those. The top left result is always the one I liked the most.</li>
<li>Stable Diffusion: cherry picked 4 results out of about 9.</li>
<li>MidJourney: I ran each prompt twice (getting 8 outputs in total), and then I picked the best 4. I didn’t try any parameters or tricks other than just using the prompt itself.</li>
</ul>
<p>I tried to be as honest as possible with the results I’m showing, because I don’t have a dog in this race. I just want something useful <em>for me</em>, wherever it comes from. You can zoom over the results to see them slightly bigger.</p>
<h3 id="what-i-look-for">What I look for</h3>
<p>The prompts I used are for stuff I am actively working on, so they’re a bit weird, slightly personal, and in some cases, oddly disturbing. Please don’t steal the prompts or the outputs from me; I can’t stop you (such is life on the internet), but it will break my heart.</p>
<p>I’ll have some more details for each prompt, and how I picked “the most interesting to me”, but the two big rubrics I looked for were:</p>
<ol>
<li>Did this model interpret the prompt?</li>
<li>Would I use one of the model’s generations “in an art” (this is very wishy-washy and not scientific; trust me, I get that)</li>
</ol>
<p>Keep in mind that:</p>
<ul>
<li>I understand that in some cases if I spent more time working on the prompt, I might get better results. The way I work is that I don’t try to force things into existing – if they don’t work out, I shelve them for a better time.</li>
<li>Some results are really weird and unsettling, and they’ve made me dislike my prompt. This isn’t the models’ faults, or their authors, nor do I have “bad feels” towards the models; it just means I’ve
accidentally created an uncanny valley and I need to back away from it until I have a better idea of what I actually want from a model. Or maybe not use bees ever again.</li>
</ul>
<h2 id="results">Results</h2>
<p>I picked 4 prompts, each of which covering a different area I am interested in:</p>
<ol>
<li>An easy to imagine concept that exists in real life (can it execute?)</li>
<li>An easy to imagine concept that <strong>doesn’t</strong> exist in real life (can it imagine?)</li>
<li>A hard to imagine concept that <strong>doesn’t</strong> exist in real life but makes sense linguistically (can it hallucinate a surrealism?)</li>
<li>Multiple concepts that exist in real life, glued together in a way that doesn’t make sense linguistically nor does it exist it real life (can it be weird?)</li>
</ol>
<p>I apologize in advance for #3 – it’s a bad place – and for the alt text on these images. Summarizing robot art is harder than I thought.</p>
<h3 id="1-linocut-print-of-a-girl-bundled-up-in-bed-with-a-stack-of-books-and-a-cat">1. “Linocut print of a girl bundled up in bed with a stack of books and a cat”</h3>
<p>What I am looking for: Something that I can actually carve into linoleum and make prints out of, so sharp lines that I don’t have to spend too much time cleaning up is ideal. The suprise winner in this category was Stable Diffusion who despite not interpreting the prompt correctly, came up with the most interesting results (in my opinion, etc)</p>
<hr />
<div class="floatie-bit">
<img alt="a grid of 2x2 images each showing a black and white drawing of a woman in bed with a cat and a stack of books. each image is slightly different but the idea is the same" src="/images/gen-models/1_dalle.webp" />
<p><b>DALL-E</b>. Composition is great (100% gets the prompt), but completely misses the mark on "linocut". I tried to work this into a useable drawing to carve, but because it uses fairly sketchy lines and fills, it ended up being way more work than I wanted.</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="a grid of 2x2 images showing sharp and crisp lines of, in order: a woman with a scarf in a library, reading a stack of books. a angry looking cat surrounded by books. a yellow cat on a pink background, wrapped around a stack of books. a cat wearing a scarf next to a smaller cat on top of a stack of books. " src="/images/gen-models/1_sd.webp" />
<p><b>Stable diffusion</b>. Composition is pretty good but doesn't actually interpret the prompt well. The "linocut" part is really well done -- I find it amazing that the top left image actually has a signature and a title outside of the print! Despite not getting the point, the top right result is my dream come true and what I'll end up using.</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="a grid of 2x2 images of simpler lines, in order: a woman with a blanket that looks a bit like a cat, with a background of books. an abstract looking cat head on top of a stack of books. sharp lines of a simplified cat head on top of a stack of books. a very abstract shape that is maybe a bed, in front of a background of books that are just white rectangles with thick outlines" src="/images/gen-models/1_mj.webp" />
<p><b>MidJourney</b>. Also kind of misses the prompt, and doesn't have as many details as the Stable Diffusion results. I really like the bottom left the most, but I don't think it screams "cat with books" enough for me to use.</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="a grid of 2x2 images where if you try really really hard it maybe looks like a woman in bed, next to stacks of books" src="/images/gen-models/1_mini.webp" />
<p><b>DALL-E mini</b>. The results are kind of okay if you squint really hard, but not at all what I'm looking for. I just got laser eyes; I'm not about to go back to squinting.</p>
</div>
<h3 id="2-lithograph-of-an-orchid-where-each-flower-has-a-small-skull-inside">2. “Lithograph of an orchid where each flower has a small skull inside”</h3>
<p>What I am looking for: any semblance of an orchid not looking like an orchid. The “lithograph” part was a very loose requirement – I just wanted it to feel “pencilly” without looking like a child drew it, which is what DALL-E often does for “pencil drawing”. I spent a <em>lot</em> of time on this prompt with DALL-E (including looking up the technical biology terms for “the bit inside an orchid flower”), and I never got anything at all correct. It wasn’t until this blog post when I went to other models that I regained hope! MidJourney, man!</p>
<hr />
<div class="floatie-bit">
<img alt="4 images, where each image is a drawing of a skull in black pencil, with an orchid coming either out of an eye socket, or wrapped around the skull" src="/images/gen-models/2_dalle.webp" />
<p><b>DALL-E</b>. I remember I tried many combinations of writing "a skull inside each flower", and
all I could ever get out of DALL-E was an orchid next to, or coming out of, a skull. I get bored after about half an hour of failing at a prompt; I'm sure there is a way to write this to get what I'm looking for, but I didn't figure it out, and I lost interest.</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="2 images of pink orchids on a different coloured background. an image of an illustration style of a skull with an orchid coming out of the middle of the skull. a vertically wrapped skull in front of the leaves of an orchid" src="/images/gen-models/2_sd.webp" />
<p><b>Stable diffusion</b>. This one struggled with the prompt too. The bottom two results are really
pretty, and in particular prettier than the equivalent (and misunderstood) DALL-E results, but still not even in the ballpark of what I was looking for</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="oof, this is a hard one. 3 of the images looks like the outline or general shape of an orchid, but in the middle some of the details resemble a skull. the fourth is a skull, but instead of the eye sockets and mouth, the shapes look like the petals of an orchid" src="/images/gen-models/2_mj.webp" />
<p><b>MidJourney</b>. I mean, 10/10. These are the spooky orchid boys of my dreams! This prompt came up because I went to an orchid exhibition, and I thought so many of the little flowers looked like skulls or aliens. This was <b>exactly</b> what I had in mind. </p>
</div>
<hr />
<div class="floatie-bit">
<img alt="4 fairly noisy aimages of an orchid next to a skull. the orchids all have leaves as well." src="/images/gen-models/2_mini.webp" />
<p><b>DALL-E mini</b>. Suffers from the same prompt problems as the other models, which makes me think
that whatever special tweaks MidJourney does to get "creativity" out of a prompt are absolutely
working.</p>
</div>
<h3 id="3-erik-johansson-photograph-of-a-womansic-hair-that-is-a-literal-bee-hive">3. “Erik Johansson photograph of a woman[sic] hair that is a literal bee hive”</h3>
<p>I love surrealism. I was watching this 60s movie where a bunch of women had beehive hairdos, and this is how my brain operates: “wouldn’t it be interesting if”? I don’t know what I was expecting to get, but it wasn’t any of this (though as someone who understands how these models work, in retrospect I understand <strong>exactly</strong> how we got here). I chose <a href="https://www.erikjo.com/work">Erik Johansson</a> because surreal photography is his jam, and it helped stir DALL-E towards more of the right vibes at the time. Unfortunately, I got <strong>really</strong> creeped out by most of the results (from all models tbh), and it’s really soured up this prompt for me.</p>
<hr />
<div class="floatie-bit">
<img alt="4 images of women's heads with either bees coming out of the hair, or the hair literally made out of bee cocoons. it's really unsettling" src="/images/gen-models/3_dalle.webp" />
<p><b>DALL-E</b>. The uncanny valley of literal bee hives turns out to be deep. The top left image one is the nicest, possibly because a) it doesn't have a face and b) it only has casual bees. It is maybe the closest to what I wanted (out of all the outputs), but it doesn't make me feel great looking at it. </p>
</div>
<hr />
<div class="floatie-bit">
<img alt="2 images of a very realistic woman, with a honeycomb or bee cocoons instead of hair. 1 image of a drawing of a woman with a body of honeycomb, next to a really big bee. one image of a closeup of a very realistic woman face that is staring aggresively, with just bees around it" src="/images/gen-models/3_sd.webp" />
<p><b>Stable diffusion</b>. The people. They look like people. I don't like it. I think the top right one is the least disturbing?</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="very illustrative, non realistic styles of a pretty woman's face, either wearing a scarf, or whose hair transitions gently into a small honeycomb" src="/images/gen-models/3_mj.webp" />
<p><b>MidJourney</b>. I find it incredibly fascinating that out of 8 images, they all have the same very specific style. Erik Johannson rarely uses people in his photography -- why is this very specific
woman coming up? Also, I think the cutesy, not really photographic style really helps these outputs, tbh. </p>
</div>
<hr />
<div class="floatie-bit">
<img alt="4 very unsettling images, that are fairly blurry. you can see the vague shape of a woman's head (sometimes including her torso), and the hair warps into a really horrible and realistic honeycomb. it truly looks creepy" src="/images/gen-models/3_mini.webp" />
<p><b>DALL-E mini</b>. Poor model, this is the worst of the bunch, and I expected it. DALL-E mini
isn't very good at realism; it gives very noisy people, or faces, and then shoves them through a potato. That, combined with (my bad) sheer
creepiness of the prompt leads to a literal nightmare.</p>
</div>
<hr />
<p>I cannot apologize enough to these models for making them go through this. You at least could’ve scrolled past this section; they, the poor darlings, couldn’t.</p>
<h3 id="4-a-toucan-wearing-a-60s-apron-sitting-on-a-mid-century-modern-armchair-talking-on-a-rotary-phone-retrofuturism">4. “A toucan wearing a 60s apron, sitting on a mid century modern armchair, talking on a rotary phone, retrofuturism”</h3>
<p>And now, a palette cleanser. I had been doing some reading and learnt that DALL-E really likes commas and stacking up contexts, so that’s why this prompt is so detailed.</p>
<hr />
<div class="floatie-bit">
<img alt="4 images of a very realistic and colourful toucan, sitting on a variety of chairs, using a rotary phone. the images look like 3d sort of artwork" src="/images/gen-models/4_dalle.webp" />
<p><b>DALL-E</b>. I expected DALL-E to do well, and it did. The toucan on the phone is there, the 60s vibe is there, the apron is dubiously missing but we'll give it a pass. It's got the fuzziness of old,
spacey, retrofuturism posters (though the prompt has absolutely no actual futurism in it)</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="4 flat illustration style images, of a toucan on a chair. on one there's a phone. on another the toucan is sitting in front of a typewriter. on another the toucan is sideways, drinking a glass of whiskey" src="/images/gen-models/4_sd.webp" />
<p><b>Stable diffusion</b>. This isn't bad either. The art style is a little flatter (what is retrofuturism even?), but it's toucans doing their thing, some better than others.</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="4 very blobby illustrations of a toucan's head. the illustrations are rich in colour, with gradients, but the toucans themselves are very abstract looking, and don't have anything other than an equally abstract looking arm chair" src="/images/gen-models/4_mj.webp" />
<p><b>MidJourney</b>. If you've ever played with MidJourney, this will strike you as having "very MidJourney vibes". This grainy, round style I see often, and I quite like. However, while it captures the style really nicely, the prompt is sort of a wash past the toucan.</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="two of the images have a toucan bird, sitting on very recognizabley 60s armchairs. the other two images have a human body with a toucan head; the body is wearing a colourful apron and is sitting in front of some 60s furniture. these last 2 images have more of a photograph or collage feel than of a drawing." src="/images/gen-models/4_mini.webp" />
<p><b>DALL-E mini</b>. This is the first time I a) love this model the most and b) wish that it produced higher resolution images. Look at the aprons! Look at the furniture! In terms of concept, it's absolute perfection. In terms of execution, an absolute potato.</p>
</div>
<h2 id="what-have-i-learned">What have I learned?</h2>
<p>I think the most important thing I’ve learnt from this experiment is that in terms of what I’m looking for (interesting hallucinations and not realism), DALL-E isn’t the end-all, be-all of models, and nor is MidJourney. The two freely available models are quite alright in some cases, especially if you’re looking for fast and free brainstorming. I think the workflow that I will try out next is to workshop the prompt using StableDiffusion/DALL-E mini, and then take that to the big boi DALL-E herself, and see what I can go from there.</p>
<p>In terms of model-specific lessons (knowing that it’s based on my weird experience with them, they’re not scientific and not necessarily applicable to what <strong>you</strong> are working on, etc. Don’t come for me, basically):</p>
<ul>
<li>MidJourney can be super creative, but can also fall into stylistic pits (see: the bees, the toucan)</li>
<li>It’s hard to get DALL-E out of a realism pit without a ton of effort (see: the skull orchids)</li>
<li>Stable Diffusion works surprisingly well for something I can run off a collab</li>
<li>I don’t have a gut feeling as to why, but pretty much everyone except for Stable Diffusion is confused by what a linocut is (this is only interesting to me, someone who works on linocuts)</li>
<li>DALL-E mini really understands what toucans want (JK)</li>
<li>I should maybe steer clear of bees.</li>
</ul>
<hr />
<p>Thanks to <a href="https://twitter.com/mrmrs_">Adam</a> for helping me rework the intro, and giving me a subtitle; I can’t believe I missed a Strangelove opp.</p>
Doing the work2021-11-18T00:00:00+00:00https://meowni.ca/posts/doing-the-work<link type="text/css" rel="stylesheet" href="/css/floatie-bits.css" />
<p>(This is a post mostly about art but I swear there’s a moral in here for all you tech readers, or at least
a discounted therapy lesson.)</p>
<p>As you may have noticed from these latest posts, at the beginning of this year I decided to “do more art”. This went really well for a couple of months: I was making new shit; I was pumped; I felt competent. Around September I stopped feeling competent and got hit with a smol existential crisis which, in retrospect, I’ve seen a million times in my engineering career. I was looking at all the artists I follow and respect and where they were in their art career and I freaked out because I was <em>nowhere</em> near their level. They all seemed to have an art voice and a big following and know exactly the kind of art they wanted to do. I 100% did, and still do not. So naturally what I took from this was that I sucked and I should just quit making art.</p>
<p>The problem with this kind of crisis is that it’s very much a “can’t see the forest from the trees” situation. The obvious response from someone on the outside would have been “Monica, stop. You’ve been literally doing this for 6 months and these people have been doing it for years. If you want to get to their level you have to just keep doing the work they did for all that time and you too will get there”. And thankfully, that’s <strong>exactly</strong> what my friend <a href="https://twitter.com/mrmrs_">Adam</a> said when I asked him to hold my feelings over zoom wine.</p>
<h2 id="productivity-is-a-fake-idea">Productivity is a fake idea</h2>
<p>Hilariously, this is also the advice I always give to the junior developers I’ve mentored in the past, but didn’t think of giving to myself. When you start in your career, or even when you start on a new team, you need to adjust your expectations of what being “productive” or “good” means, or else you’re going to have an emotionally bad time. Your team lead that has been working on that team for 3+ years will obviously know more about it than you. Your friend who’s been writing JavaScript for 10 years will most likely write JavaScript faster than you, if you’re new to it. Someone who runs marathons will run longer without dying than me, a person who runs out of breath going up a hill.</p>
<p>These are only value judgements if they meet your priorities and expectations. Not being able to run a marathon does not make me a lesser artist (but it does make a lesser athlete, a metric I am blessedly not interested in pursuing). Not being able to write JavaScript makes me a worse engineer only if my expectation is to be a senior JavaScript engineer, but not if my expectation is to be a junior SQL engineer. This is why “being junior” isn’t a value judgement – you can be a fantastic and overachieving junior engineer, but still a junior because you’ve been only doing it for 3 weeks.</p>
<p>So what I was feeling wasn’t actually “I am shit and I should quit this” but “for my current priorities, I feel like a lesser artist because my expectations are much higher than my current technical abilities”.</p>
<p>The way you get out of this rut is either by adjusting your expectations (“I don’t want to be a good at SQL so it’s fine I only know how to do a select *”, a thing I have said out loud before), or adjusting your experience (“in order to be a better runner I should start small with a couch to 5k program, and stick to it to get used to it”, a thing I have absolutely never said but immensely respect anyone who has).</p>
<h2 id="quality-vs-quantity">Quality vs. quantity</h2>
<p>Adam also told me <a href="https://austinkleon.com/2020/12/10/quantity-leads-to-quality-the-origin-of-a-parable/">an anecdote</a> about a ceramics professor who told half of their class they’d be graded on the amount of work they produced, and the other half that they’d be graded on one thing they picked as “the best thing” they’ve made. Spoilers: at the end of the year, the people who were graded on quantity also produced ceramics that were of higher quality than the other group’s. Churning out work leads you to learn from mistakes, get better at things mechanically, so that you can focus more on the rest of the bits. Once you have the basic JavaScript syntax down and you don’t panic at writing a for-loop, you’re more confident that you are a programmer. Feels obvious right?</p>
<p>Amazingly, it didn’t feel obvious to me last month, even though in 2018 I went through a <strong>very</strong> similar existential crisis and “solved” it by churning out <a href="/posts/inktober/">a whole inktober project</a>.</p>
<h2 id="now-do-the-work">Now do the work</h2>
<p>This is your sign that if you’re feeling equally down about a skill you expect, or want, to be better at, you should probably sit down and make a plan about how to level up. For me, it looked like this:</p>
<div class="floatie-bit">
<img alt="a grid of 3 columns, 4 rows, where each cell is shaded with something like vertical lines, a cross hatch, dots, squiggly lines, etc." src="/images/2021-11/plotter.webp" />
<p><b>Step 1</b>. Improve the basics: get better at writing JavaScript that I can then use when I do make art; have a better set of basic utilities and examples. Look at all the ways I can now fill boxes, when before I could only fill them with solid colours or gradients!</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="a grid of 8x8 cells where each cell is filled with a random number of squiggly lines" src="/images/2021-11/1.webp" />
<p><b>Step 2</b>. Keep churning things. I started with a fixed grid, and filled every cell with every new shading style I wrote, to see which look cool together.</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="a recursively subdivided grid, where each cell is filled with many parallel straight lines at 45 degrees" src="/images/2021-11/2.webp" />
<p>What if the grid isn't even? Shout out to the grid og, Mondrian.</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="a grid of 8x8 cells, where each cell is a quarter of a circle, in any of the 4 orientations, filled with a grid of dots" src="/images/2021-11/3.webp" />
<p>What if the grid isn't of squares?</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="a recursively subdivided grid where each cell is filled with a set of two or three either horizontal or vertical rectangles with squiggly outlines. this alt is very bad and i am sorry." src="/images/2021-11/4.webp" />
<p>What if each cell is a grid?</p>
</div>
<hr />
<p>None of these are amazing, and I wouldn’t call any of them “art”, but it does mean that with minimal effort I went from this generative landscape on the left that I wasn’t particularly inspired by, to this one on the right that is looking like it might go somewhere. More on these landscapes when I finish <strong>step 3</strong>: stick with a project until it is done, not until you get bored of it (because doing the work is never as exciting as starting something new).</p>
<div class="floatie-bit">
<img alt="a very abstract looking landscape of 5 overlapping mountain ranges and a big sky. they're all filled in solid colours." src="/images/2021-11/land1.webp" />
<img alt="a similar landscape but each mountain range is filled by a grid of black dots of different densities." src="/images/2021-11/land2.webp" />
</div>
A HashiConf art collab2021-10-15T00:00:00+00:00https://meowni.ca/posts/hashicollab<style>
.floatie-bit {
display: flex;
flex-direction: row;
}
.floatie-bit > p { margin: auto; padding-left: 24px;}
.floatie-bit > img {
width: 35%;
flex-shrink: 0;
vertical-align: middle;
border-radius: 10px;
}
.floatie-bit > .img-column {
width: 30%;
flex-shrink: 0;
display: flex;
flex-direction: column;
margin-right: var(--spacing-s);
}
.img-column img {
width: 90%;
vertical-align: middle;
border-radius: 10px;
}
@media (max-width: 700px) {
.floatie-bit {
flex-direction: column-reverse;
}
.floatie-bit > img {
margin-top: 24px;
width: 80%;
}
.floatie-bit > p {
padding-left: 0;
}
.floatie-bit > .img-column {
flex-direction: column;
width: 100%;
margin-top: var(--spacing-s);
align-items: center;
}
.floatie-bit > .img-column > img {
width: 80%;
padding-top: var(--spacing-s);
}
}
</style>
<p>A couple of months ago my friend <a href="https://twitter.com/janaboruta">Jana</a> helped organize <a href="https://hashiconf.com/europe/">HashiConf Europe</a>, and asked me to work on generating custom artwork for each of their speakers. This was my first experience with creating art for someone else, and especially art that had to match someone else’s artistic guidelines. I’ve said this before, but I live and nap by the idea that rules (and editing) are at the core of the artistic process and not having to do that myself was <span class="hilite">brilliant</span>. I wanted to write a bit about it because a) I did a bunch of work that I’m really proud of and I never got to talk about it and b) I am vain and I want to talk about it.</p>
<p>This was the final poster:</p>
<div class="blockquote">
<img alt="a poster for a person named minnie mouse that consists of a grid of triangles and letters in the name, and then in the bottom part of the poster, the name minnie mouse in a big font, with a hashiconf europe footer" src="/images/hashicollab/final_poster.webp" />
</div>
<p>And this is what we wrote about it on the card that accompanied it:</p>
<blockquote>
<p>Enclosed you will find an art print made for you by generative artist and engineer Monica Dinculescu. <br /><br />
This individual print is unique to you and you alone, as Monica has incorporated your name, the title of your talk, and the color of the HashiCorp product that you are talking about at this year’s conference.<br /><br />
Each cell in the grid has been randomly generated to contain either a letter from your name, or a pattern in the product color. <br /><br />
Cells located towards the middle of the grid are more likely to contain a letter rather than a pattern, and this likelihood decreases in the cells towards the edges. <br /><br />
This print is the only iteration of its kind. Were it to be generated again, it would look slightly different each time.</p>
</blockquote>
<p><br /><br />
I print everything in my <a href="https://www.meownica.studio/">art store</a> myself, because I sell very limited editions and third-party printing companies only make sense financially when you’re printing in the several dozen. This does mean that sometimes I’ll fight with my printer thinking that its colour profile is whack, only to discover that it was Doing Just Fine™️ and my MacBook oversaturates things like it’s an Instagram filter in 2011. Every day is a school day.</p>
<p>Here are all the prints drying on their totes profesh drying rack that is definitely not just a string I hung around in my office.</p>
<p><img alt="a photo of a bunch of posters drying, each poster hanging off a string with a paper clip" src="/images/hashicollab/drying.webp" /></p>
<p>So, how did we get here?</p>
<h2 id="v1-i-have-no-idea-what-you-want">v1: I have no idea what you want</h2>
<p>The design team’s requirements were clear and wonderful:</p>
<ul>
<li>follow the conference guidelines: there is a set of product colours that can be used in very specific ways (either as a solid colour, a gradient, a grid of dots, hashed lines, and nothing else) and should be used to have a geometric grid feeling.</li>
<li>the art should be unique and somewhat meaningful to the speaker. Like, random shapes might look beautiful, but they have no immediate connection to the speaker or what they were talking about, and needing to explain it too hard takes away from the magic of someone making you your very own art.</li>
</ul>
<p><br />
When I think of geometric posters, I (and everyone who’s ever looked at a design book) think of <a href="https://mymodernmet.com/what-is-bauhaus-art-movement/">Bauhaus</a>, and that’s exactly where I started. Here’s some iterations from this round:</p>
<p><br /></p>
<div class="floatie-bit">
<img alt="a grid of randomly rotated quarter circles" src="/images/hashicollab/v1_1.webp" />
<p>Too Partridge-Family: I started with a basic Bauhausy sort of grid, but we felt that it looked too much like a grid of birds. Not all was lost, though; you can see this in a different style on the home page now!
</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="a grid of squares, some of which are subdivided recursively up to a depth of 3" src="/images/hashicollab/v1_2.webp" />
<p>Extremely Mondrian-y, divided grid of squares. Really boring.
</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="a grid of randomly rotated quarter circle arcs" src="/images/hashicollab/v1_3.webp" />
<p>Random paths on a grid. These are called <a href="https://en.wikipedia.org/wiki/Truchet_tiles">Truchet tiles</a>, and they're a pretty classic generative art approach. I thought this looked really cool by itself, but it didn’t have a ton of connection with the speaker so we scraped them early on.
</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="a grid of random shapes (rectangles, squares, circles, lines) randomly positioned" src="/images/hashicollab/v1_4.webp" />
<p>Random shapes in random places. Too generic, kinda boring.
</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="a grid of mostly rectangles, a couple of lines and a triangle that are mostly positioned towards the top right corner" src="/images/hashicollab/v1_5.webp" />
<p>Random shapes in random places but channeling <a href="https://en.wikipedia.org/wiki/Suprematism">Suprematism</a>. I LOVE this style and I would LOVE to use it again, and I very well might.
</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="a grid of squares and some letters positioned randomly, sometimes overlapped by one of the squares. nothing is readable." src="/images/hashicollab/v1_6.webp" />
<p>Grid of squares with some overlapped letters. This was one we liked because it had both a grid and the speaker's name, but it felt a bit too chaotic in this iteration. It honestly looks to me like I sneazed a bunch of letters on a grid.
</p>
</div>
<hr />
<p>At this point I also considered some data-based art, but I didn’t have enough interesting data that I could aggregate based on a name and an abstract alone. Anyway, did you notice how a lot of the iterations in this bunch are fairly standard generative-art approaches? Like, nothing here was novel, and I intended it that way: I wanted to figure out what the team had in mind, and the only way I knew how to do that was to use some common language we all knew, which is “things we’ve seen before”.</p>
<h2 id="v2-grids-of-letters">v2: Grids of letters</h2>
<p>What came out from the previous explorations was that they liked the letter grid the most, but not as it was. At this point I also learnt that a specific colour (like, blue) is associated with a particular product (in blue’s case that product is <a href="https://www.vagrantup.com/">Vagrant</a>) that a speaker is giving a talk about. Each of these products have a primary and a slightly lighter secondary colour, which is why below you’ll see I stopped mixing different product colours together.</p>
<hr />
<div class="floatie-bit">
<img alt="a grid of hatched squares or circles, some of which are subdivided recursively up to a depth of 3.sometimes there's a hatched circle that overlaps a subdivided cell" src="/images/hashicollab/v2_6.webp" />
<p>Can we do something with that Mondrian-y grid, but make it less boring with textures? Answer: yes but not in a good way.
</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="a name in the center of the image overlapping randomly positioned rectangles and squares, which are hatched" src="/images/hashicollab/v2_5.webp" />
<p>Can we do something with those random shapes in random places, but by also remembering the user has a name? Answer: no and let's never mention this horror again.
</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="a grid of connected blobs, with some of the cells also containing a letter in the name" src="/images/hashicollab/v2_3.webp" />
<p>Enough of that; let's go back to our friend, the grid. Truchet tile blobs with letters. I vetoed this before even sending it over because it was serving very solid 70s lava lamp vibes. At this point I also went a bit mad looking at just Jana's name, so I branched out to another team member.
</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="a grid of squaares, with some of the cells being a letter in the name" src="/images/hashicollab/v2_1.webp" />
<p>Grids of squares with letters. A bit boring, a bit "not a fan of cutting off letters on principle". This image is really good at showing the letter distribution algorithm that all the grids are using: it's mostly centered around the center horizontal line, and then spreads out randomly outwards and downwards from there.
</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="a grid of randomly rotated half circles, with some of the cells being a letter in the name" src="/images/hashicollab/v2_2.webp" />
<p>Grids of Bauhaus-style quarter circles or letters. I kind of like this, but overall it still felt a bit too basic and too “classic Bauhaus”.
</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="a grid of triangles, with some of the cells being a letter in the name" src="/images/hashicollab/v2_4.webp" />
<p>Grids of triangles or letters. This one looks kind of nice, doesn't it? We thought so too.
</p>
</div>
<hr />
<p>You’ll notice that all of these images are very similar looking. This was an extremely good sign for me because I knew we were on the right track, and in the cleaning up stage.</p>
<h2 id="v3-bring-it-home">v3: Bring it home</h2>
<p>We all loved the triangles the most, and in order to make them a bit 🌶 spicy, I decided to vary the texture of the triangle. Remember, I was allowed dots and hatches too! So in the final version, each triangle can be one of the 2 colours belonging to the product, either hashed or filled. Bish, bash, bosh.</p>
<p>From here it was just a matter of designing the poster layout around it, and making sure I had a plan for when my random number pixel math wasn’t quuuuuuuite good enough. Thankfully, <a href="https://github.com/dataarts/dat.gui">dat.gui</a> exists, and this is what I looked at while generating 50-ish posters:</p>
<p><img alt="an image of a browser showing one of the randomly generated outputs (the one used in the final poster). to the right there is a control panel containing options such as font size, text offset, box offset, spread, draw text box, etc. the names don't make a ton of sense" src="/images/hashicollab/browser.webp" style="border: 2px solid #F6AFA7; padding: 0;" /></p>
<h2 id="would-i-do-this-again">Would I do this again?</h2>
<p><span class="hilite">Absolutely</span>. I honestly loved every step of this. I worked with 3 amazing women from start to finish that had good instincts and were kind and supportive af. I got to think outside of a box I wouldn’t have normally found myself in. I tend to obsessively listen to a new album for 100 hours and I exclusively listened to Jessie Ware for this project (and then, ironically, never again).</p>
<p>Most importantly, I made something that is in 46 humans’ hands. I hope they liked it!!!</p>
<p>I would love to do this again, so if you’re a conference or a company that thinks this was a
cool idea and want to do this for <i>your</i> speakers or employees, <a href="mailto:mdinculescu@gmail.com">send me an email</a> ! I’ve got way more ideas where this came from and nothing to do with them.</p>
Week 32: putting these on pause2021-08-16T00:00:00+00:00https://meowni.ca/posts/week-32<p>It’s been <del>one week since you looked at me</del> 8 months since I’ve been writing these weekly notes, and
I think I’m going to take a small break from it. It’s been a good experiment, but I’ve been getting sloppy with remembering they’re due, and it’s starting to feel a bit self-indulgent. Because I don’t actually advertise that they exist, I don’t think anyone reads them, so I’m not entirely sure why I’m writing them if nobody on either side of the fence is particularly amused by the experience. I do have a blog post in the barrel which is a rare and momentuous occasion y’all can look forward to.</p>
<p>xoxo,
monica</p>
Week 30: this little drummer girl2021-07-26T00:00:00+00:00https://meowni.ca/posts/week-30<ul>
<li>On Monday Frances took me to the teamLab exhibition at the Asian Art museum. It was really wonderful! I hadn’t seen the one in Japan so I was new to it, but the interactive art nerd in me was all over the concept and trying to figure out what was static and what was interactive, and where the sensors were. Tons of fun!</li>
<li>On the weekend we went to the Gold Cup ⚽️ final in Vegas. This is important only because: the powers that be let me bring my own drum and I <a href="https://twitter.com/AmericanOutlaws/status/1422234579436457986?s=20">drummed for the whole 120 minutes</a>. As I might have mentioned this has been a dream of mine, and I’m 100% that dream-believe-achieve bitch. I still need to improve a lot, and bless Ryan (who played the big boi drum next to me) who had to put up with me coming in sliiiightly too early a lot. Only up from here!</li>
<li>Looking at my calendar, a lot of my funemployment is spent levelling up. This will not shock anyone but: I kind of loved school. My first 2 years in uni I audited like 2 or 3 extra classes a semester, because I’d get all the bits I loved (going to class, learning things, taking notes), and none of the existential dread and panic attacks of being evaluated. So now that I’m an adult who gets to control how I learn things, I’m all over it. Here’s what’s currently on the docket:</li>
<li>Practicing drum rudiments on my snare drum. I should probably find a human to pay to help me with this, since I’m figuring it all out from YouTube and tbh, I have some questions.</li>
<li>Singing classes. I really enjoy singing in the car but I a) don’t like the sound of my voice and b) don’t actually like to produce loud sounds that draw attention to me, both of which make me not
want to sing along in front of people. A lot of singing is relearning how to breathe, and that is Extremely Weird.</li>
<li>Spanish on Duolingo. I used to speak it as a kid (I used to watch a lot of telenovelas, and I’m a bit of a language sponge), but I lost it since I never actually used it. I want to be able to speak to my friend’s Colombian family when we go to their wedding, so here I am.</li>
<li>Conversational French hangouts on italki. French and I have a super uncomfortable relationship where after 11 years in Montreal I understand and can read most of it, but I’m unable to speak it. It’s not that I don’t know what to say, it’s that I literally panic and refuse to say the words. I’ve had a lot of kind of traumatic experiences with a bunch of french (and quebecois) native speakers who were really hostile to people who speak French poorly, and it left me a bit terrified of even trying. I figured the best I can do to work on it is pay a complete stranger to talk to me – even if they think I’m shit, I won’t ever see them in person. I will be able to sleep at night just fine.</li>
<li>Books: I am struggling with Homegoing by Yaa Gyasi, because it’s a devastating book. It’s well written and I desperately want to finish, but I find myself dreading reading it; all the stories are about horrible things that people did to slaves and my heart just breaks on every page.</li>
</ul>
Week 29: I do some actual work for a change2021-07-19T00:00:00+00:00https://meowni.ca/posts/week-29<ul>
<li>First off: I think I’ve done a bad numbering in some of these recent weeks, because I wanted to write about what I actually did on July 13/14, but apparently I’ve already counted that week? Counting, man, a hell of a thing.</li>
<li>My good friend <a href="https://www.maeghansmulders.com/">Maeghan</a> who aside from being a top drawer human being and tswift fan is an amazing professional contact (and I don’t deserve her one bit), connected me
with the European Innovation Academy (<a href="https://www.inacademy.eu/">EIA</a>) to do some technical mentoring for them. They run a pretty neat incubator-like program, where they teach university students how to get from startup idea to feature work, to sort-of implementing and finally pitching to VCs</li>
<li>I was a bit terrified of doing this because I think of myself as having no startup experience, but it turns out a lot of the things I learnt running small projects inside of Google really apply and I was kind of really good at it? I know, self confidence, who saw this coming.</li>
<li>Related: I’m trying to be a little less self-deprecating professionally. I deal with social anxiety by telling jokes and being wildly self-deprecating, but I’ve noticed when people just meet me and don’t know me at all, they don’t know I’m joking and I come off like an incompetent weirdo. This is a big change for me because I hate confrontation and being assertive, but I need people to know I am actually a badass. C’est la vie.</li>
<li>Observed anecdote: the teams that were predominantly women were on top of their shit and kicked ass. The teams that were predominantly men, OR had a very loud and uninformed men in charge were super behind and not doing super well. This makes sense because we know women are particularly good at organization and doing homework, but tragic because we also don’t have a ton of women leading startups.</li>
<li>We were in Tahoe for the week and my local friend Jessica somehow tricked me to go to 3 (THREE) days of yoga classes. Two of them were hot and disgusting, and one of them was aerial! What’s even more disgusting than the amount of sweating that I did is that I enjoyed them? Who am i.</li>
</ul>
Week 28: I did a vacation2021-07-12T00:00:00+00:00https://meowni.ca/posts/week-28<ul>
<li>So here’s what happened: I went on vacation and because I’m leaning so hard into this funemployment life I didn’t open my laptop once.</li>
<li>Every year Zach and I go to his parents’ (now my in-laws! hah!) lake in Minnesota for the 4th of July. Normally we fly there, but last year we drove (because of the panderoni) and this year we also drove because you can’t fly with a big dog anymore. It’s kind of a bummer, and I hope airlines revisit this decision and find a middle ground that works both for the rest of the plane and people with nice but big dogs that don’t fit under seats (like …buying your dog their own seat. I’d be fine with that).</li>
<li>Anyway, this all to say: we drove for 3 days again from SF to Minnesota (and then 3 days back). This year was a bit easier because I (finally) got my US driver’s license so Zach didn’t have to drive 9 hour days by himself. Also we didn’t camp or stop for sightseeing because we’ve exhausted everything of note in the middle of America.</li>
<li>Related: Montana is enormous. I feel we drove an entire day through Montana and there was still some Montana left.</li>
<li>Having now driven SEVERAL hours on the highway: truck drivers are wild. Some started signalling and pulling in my passing lane as I was trying to pass <em>them</em>; some do dick moves like drive parallel with another truck so nobody can pass them; almost none of them respect the 55 speed limit in construction zones. Rules? Who is she.</li>
<li>Something I should’ve predicted is that I emotionally implode with anxiety while driving thinking that the people behind me are judging me. Do they think I’m weak because I’m only driving the posted limit in this construction zone? Don’t they know fines double?? Should I be speeding? I know I’m driving a Tesla but I’m an old woman at heart who would love it if we could all just follow the rules, ok?</li>
<li>Both the dog and I had a great time at the lake. She spent all day in the lake swimming and playing with my 5 year old nephew; she also tried to play with the A, 3 year old, but their motor skills didn’t match well. A would try to throw a buoy for her, but she couldn’t throw it well, so Hopper thought this was actually an invitation to play tug and would accidentally hip check A, who would then trip, drop the buoy, get excited, pick up the buoy again and repeat this until bored. Hopper was exhausted every night and slept like a rock. I also slept like a rock because kids babysat my dog and unlike previous years I had zero work stress on my soul.</li>
<li>I read nonstop while sat in the sun with industrial amounts of sun screen on. I did my yearly penance of watching nightly baseball because it was on, saw the Habs make it and then lose the Stanley cup (it’s fine, I’m not heartbroken), watched (on purpose) the Euros and Wimbledon, ate a lot of cheese curds.</li>
<li>Books: Parable of the Sower (loved it, strangely apropos for 2020s fire and drought California), The invisible life of Addie Larue (TikTok made me read it, great beach read), Klara and the Sun (allegedly a sci-fi but not in like a Dune or aliens kind of way), Little Weirds (did not finish it; didn’t enjoy the writing style AND my book was badly bound making it a frustrating discovery). I’m now reading Homegoing which is shaping up to be great.</li>
<li>A weird thing that happened on the drive back is that I had a medium-bad allergy to (I think) peanut butter. I broke out in hives and now I need to either get that shit checked out or do another test at home with an epi-pen nearby because in general only bad things follow hives. Ugh.</li>
<li>Did y’all have a summer break? Was it good? Maybe tell me about it on Twitter?</li>
</ul>
I redesigned my site2021-06-24T00:00:00+00:00https://meowni.ca/posts/redesign<p>The last time I changed something big on my site was 3+ years ago. I was really into gradient rainbow text and IBM Plex Mono. It was a mild increment over whatever I had before, which was just a cleaned up and colourful version of <a href="https://zachholman.com/left/">Left</a> from like 6 years prior. Anyway, what I’m saying is: this website had hella split ends. Here’s the before and after:</p>
<style>
.arrow {
font-size: 40px;
text-align: center;
font-weight: bold;
}
</style>
<div>
<img alt="my old website. a column of text, with a navbar at the top, and rainbow h1 headers" src="/images/redesign/before.jpg" />
<div class="arrow">↓</div>
<img alt="my new website. the structure of the site is the same, but below the nav bar there are
now 2 columns: the left one has a small blurb about me, the right one has a grid of colourful cells." src="/images/redesign/after.jpg" />
</div>
<h1 id="but-also-why">But also, why?</h1>
<p>I used to write a lot of blog posts, so my website was very blog focused. Originally the home page was just a listing of posts with some copy sprinkled in. As the pandemic hit, I started writing fewer and fewer posts, so having the one post of 2020 show up as a listing was ridiculous.</p>
<p>On top of that, this year I started focusing a lot more on making generative art and I thought it was sad I wasn’t showing that anywhere on the site! The beauty of generative art is that you can just keep generating it over and over again, and my site was a long white column of text. No more!</p>
<h1 id="git-diff">git diff</h1>
<p><img alt="+1580 lines -23906 lines" src="/images/redesign/diff.png" />
Ok so hear me out, this looks bad and that’s because: it is bad. One cute thing my old site had was a <code class="language-plaintext highlighter-rouge"><emoji-rain></code> custom element. You pressed it and got a rain of emoji. Brilliant. The problem is that
I wrote that in like 2017, and used the well deceased v0 version of the web components spec. As a result, it had a gazillion dependencies (rip <code class="language-plaintext highlighter-rouge">bower_components</code>) and polyfills and ancient trash. 23906 lines of trash to be exact. Gooood riddance.</p>
<p>The added 1580 lines are a bit of a bore:</p>
<ul>
<li>100 of these are this post.</li>
<li>another 300 is the <a href="https://css-tricks.com/new-site-html-ipsum/">HTML Ipsum</a> from CSS tricks because I’m always scared I’m going to break a style on some ancient post.</li>
<li>300 lines of JavaScript for the header art that I don’t minify because I’d like to be able to read this in the future to fix whatever mistakes past Monica made, and I don’t have a build step set up. I’m still using the Jekyll that comes out of the box with GitHub pages and I am never looking back.</li>
<li>the rest is actual code that I touched! There was some really horrifying markup on the project/talks/template pages and now it’s uhhhh slightly less so.</li>
</ul>
<h1 id="lighthouse">Lighthouse?</h1>
<p><img alt="lighthouse scores all green" src="/images/redesign/lighthouse.png" />
Seems fine. The 93 is because I’m running this on localhost, where I don’t have <code class="language-plaintext highlighter-rouge">https</code>. I’ll double check
it once after I deploy, but that 96 on performance is sitting fine with me given that I (again) don’t minify anything on this site.</p>
<p>I still don’t have a service worker installed because last time I did it cached things into oblivion, and I make hella typos on my posts and it was a bad time. Who even reads this site? Why would you want it offline? You don’t.</p>
<h1 id="colours">Colours</h1>
<p>I tried <a href="https://twitter.com/notwaldorf/status/1407102532992655361?s=20">really hard</a> to add some dreamy pastel blobs randomly in the page. As you can tell from the replies on that tweet, it went to the Bad Place. The thing that worked the most was “blurred spans positioned absolutely” which makes browsers and my soul cry. I also didn’t want to use images, because … I didn’t. Downloading bytes of background images? In this economy??</p>
<p>With pastels out of the way, I went for the exact opposite: <span class="hilite">bright</span> and
<span class="dotted">patterned</span>. I recently worked on a Bauhaus inspired project for HashiConf (which ended up in the end not looking very Bauhaus at all), so I had a bunch of unused code lying around. <a href="https://en.wikipedia.org/wiki/Bauhaus">Bauhaus</a> aesthetics are all about grids and bold, simple colours – I don’t know if I did it justice, but I certainly tried.</p>
<p>The blog and weaknotes pages are listings of links which I always struggle with and 2021 didn’t fix
that. If all the text is a link, and links are meant to be bright, then most of the page is a bright, unreadable scream of colours. So on these pages, the default href styles are gone, and I added some hatching on hover only. That way there’s splotches of colour, but they’re mostly out of the way.</p>
<h1 id="the-generative-art-bits">The generative art bits</h1>
<p>I experimented with a bunch of basic generative art algorithms for the pages, like animated <a href="https://genekogan.com/code/p5js-perlin-noise/">Perlin noise</a> and grid-based randomness. In the end, I did a variation on a pretty standard <a href="https://en.wikipedia.org/wiki/Truchet_tiles">Truchet tile</a> grid that you see a lot in beginner generative art tutorials: you have a grid of cells, and each cell can be one of the 4 orientations of a quarter-circle. After staring a bunch at these quarter circles, some started looking like objects: a tulip, a boat, a bird. I kept one of each and painted them in black, so that they pop out.</p>
<h2 id="an-accidental-p5js-mini-polyfill">An accidental p5.js mini-polyfill</h2>
<p>I do all of my generative art in <a href="https://p5js.org/">p5.js</a> and <a href="https://github.com/mattdesl/canvas-sketch">canvas-sketch</a> because I want to write the least amount of canvas code I can, and most of the time I do that offline and for printing on paper. In this case though this code would live on every single page, and p5.js comes in at a couple hundred kb, most of which I am not actually using. I am a lazy woman with a lot of napping to do; I don’t want to rewrite any of my already written p5.js code! So I ended up writing a <a href="https://gist.github.com/notwaldorf/e63ea117011ac059a258776d65b6ffc3">little small object</a> that implements only the bits of the p5.js API I was using. Cute eh?</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">context</span><span class="p">;</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">p</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">PI</span><span class="p">:</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">PI</span><span class="p">,</span>
<span class="na">HALF_PI</span><span class="p">:</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">PI</span> <span class="o">/</span> <span class="mi">2</span><span class="p">,</span>
<span class="na">random</span><span class="p">:</span> <span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">a</span> <span class="o">!==</span> <span class="kc">undefined</span> <span class="o">&&</span> <span class="nx">b</span> <span class="o">!==</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="nx">b</span><span class="p">)</span> <span class="o">+</span> <span class="nx">a</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="c1">// Assume a is an array.</span>
<span class="kd">const</span> <span class="nx">i</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="nx">a</span><span class="p">.</span><span class="nx">length</span><span class="p">);</span>
<span class="k">return</span> <span class="nx">a</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="na">fill</span><span class="p">:</span> <span class="p">(</span><span class="nx">c</span><span class="p">)</span> <span class="o">=></span> <span class="nx">context</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="nx">c</span><span class="p">,</span>
<span class="na">stroke</span><span class="p">:</span> <span class="p">(</span><span class="nx">c</span><span class="p">)</span> <span class="o">=></span> <span class="nx">context</span><span class="p">.</span><span class="nx">strokeStyle</span> <span class="o">=</span> <span class="nx">c</span><span class="p">,</span>
<span class="na">noFill</span><span class="p">:</span> <span class="p">()</span> <span class="o">=></span> <span class="nx">context</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">transparent</span><span class="dl">'</span><span class="p">,</span>
<span class="na">noStroke</span><span class="p">:</span> <span class="p">()</span> <span class="o">=></span> <span class="nx">context</span><span class="p">.</span><span class="nx">strokeStyle</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">transparent</span><span class="dl">'</span><span class="p">,</span>
<span class="na">push</span><span class="p">:</span> <span class="p">()</span> <span class="o">=></span> <span class="nx">context</span><span class="p">.</span><span class="nx">save</span><span class="p">(),</span>
<span class="na">pop</span><span class="p">:</span> <span class="p">()</span> <span class="o">=></span> <span class="nx">context</span><span class="p">.</span><span class="nx">restore</span><span class="p">(),</span>
<span class="na">translate</span><span class="p">:</span> <span class="p">(</span><span class="nx">x</span><span class="p">,</span><span class="nx">y</span><span class="p">)</span> <span class="o">=></span> <span class="nx">context</span><span class="p">.</span><span class="nx">translate</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span><span class="nx">y</span><span class="p">),</span>
<span class="na">scale</span><span class="p">:</span> <span class="p">(</span><span class="nx">x</span><span class="p">,</span><span class="nx">y</span><span class="p">)</span> <span class="o">=></span> <span class="nx">context</span><span class="p">.</span><span class="nx">scale</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span><span class="nx">y</span><span class="p">),</span>
<span class="na">rect</span><span class="p">:</span> <span class="p">(</span><span class="nx">x</span><span class="p">,</span><span class="nx">y</span><span class="p">,</span><span class="nx">w</span><span class="p">,</span><span class="nx">h</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">fillRect</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">w</span><span class="p">,</span> <span class="nx">h</span><span class="p">);</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">strokeRect</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">w</span><span class="p">,</span> <span class="nx">h</span><span class="p">);</span>
<span class="p">},</span>
<span class="na">circle</span><span class="p">:</span> <span class="p">(</span><span class="nx">x</span><span class="p">,</span><span class="nx">y</span><span class="p">,</span><span class="nx">d</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">beginPath</span><span class="p">();</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">arc</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">d</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">PI</span> <span class="o">*</span> <span class="mi">2</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">fill</span><span class="p">();</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">stroke</span><span class="p">();</span>
<span class="p">},</span>
<span class="na">arc</span><span class="p">:</span> <span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">w</span><span class="p">,</span> <span class="nx">h</span><span class="p">,</span> <span class="nx">start</span><span class="p">,</span> <span class="nx">stop</span><span class="p">,</span> <span class="nx">slice</span><span class="o">=</span><span class="kc">false</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">beginPath</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">slice</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">moveTo</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">arc</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">w</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span> <span class="nx">start</span><span class="p">,</span> <span class="nx">stop</span><span class="p">);</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">closePath</span><span class="p">();</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">fill</span><span class="p">();</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">stroke</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Then you get to write your <code class="language-plaintext highlighter-rouge">setup</code> and <code class="language-plaintext highlighter-rouge">draw</code> functions in almost the same way you would in p5.js.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">context</span><span class="p">;</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">p</span> <span class="o">=</span> <span class="p">{...}</span>
<span class="kd">function</span> <span class="nx">mySketch</span><span class="p">(</span><span class="nx">p</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">function</span> <span class="nx">setup</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="nx">draw</span><span class="p">();</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">draw</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">//</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// "Instantiate" our weird "p5.js" "instance" mode.</span>
<span class="nx">mySketch</span><span class="p">(</span><span class="nx">p</span><span class="p">)</span>
</code></pre></div></div>
<p>Woof, you made it this far? Slow news day, eh?</p>
Week 25: I redid my website2021-06-21T00:00:00+00:00https://meowni.ca/posts/week-25<ul>
<li>It’s true, I did. I also wrote <a href="https://meowni.ca/posts/redesign/">a blog post</a> about it because
tbh I did a good job on it, and it’s a good read.</li>
<li>As two ladies of leisure, Frances and I went to the MOMA on a weekday to see the new Nam June Paik exhibition. I didn’t love all of it, but the bits I really enjoyed were all about putting TVs in unexpected situations. Which makes sense, since I really enjoy when surrealism is executed well. I loved <a href="https://sfmoma-media-dev.s3.us-west-1.amazonaws.com/www-media/2021/01/06155432/02-Nam-June-Paik-TV-Garden-768x512.jpg">the TV garden</a> which was a dark room of real life plants with a bunch of old school CRTs showing … weird ass videos; <a href="https://sfmoma-media-dev.s3.us-west-1.amazonaws.com/www-media/2021/01/16103830/14-Peter-Moore-Charlotte-Moorman-with-TV-Cello-and-TV-Eyeglasses-768x965.jpg">the TV cello</a> and <a href="https://walker-col.imgix.net/wac_5538.tif?fm=jpg&w=1440&h=1050&fit=max&dpr=1.5">TV bra</a> which are exactly what they say they are, and a bunch of TV sculptures, like this <a href="https://sfmoma-media-dev.s3.us-west-1.amazonaws.com/www-media/2021/01/13165445/16-Nam-June-Paik-Merce-Digital-768x940.jpg">TV robot</a>.</li>
<li>The museum still requires indoor masks, which I have nothing against but thought it was an interesting choice since most of SF has given up on indoor masks for the vaccinated and the whole point of museums is to like… not touch anything, people or art. Anyway, I bought a dope book from the gift shop about architectural form that (as I am writing this entry 3 weeks later) I have yet to open.</li>
<li>Speaking of architectural form, the MOMA has a bonkers layout and I can never find the stairway that goes in the direction I need. I always end up wandering aimlessly around like a chicken. I have nothing against wandering aimlessly through a museum, I just don’t like it to be in a quest for stairs.</li>
<li>I bought a Diana Instant camera!!! Suz was the one who bought one first and tweeted about it, and it reminded me about how when I was poor in uni and “into photography” I was all about plastic leaky cameras (like Holgas and Dianas) but they were mad expensive and I needed to spend my money on predominantly food and wine. But now they’re an afforable price for adult Monica AND they’ve made an instant version that works with the Instax film so you don’t have to wait for 3 weeks to see you took a tragically exposed photo. Suz and I are now having this paired adventure where we text each other bad photos and compare notes. It’s fun.</li>
</ul>
Week 24: vroom vroom2021-06-14T00:00:00+00:00https://meowni.ca/posts/week-24<ul>
<li>Weird flex: been making cheese fondue for lunch because I make the rules here.</li>
<li>One of the (many) amazing things about being a Canadian living in California is that <em>this</em> state (unlike, say, no-rules-nevada) doesn’t recognize non-US driving licenses. This means that after almost 20 years of
getting my first driver’s license I had to do the whole circus show (written test, road test,
being treated like a teenager, etc) to be able to drive here. This week I finally bit the bullet
and a) drove daily which is wild and b) did my road test (passed with flying colours, I know you’re
wondering). Side effect: I don’t have to use my Quebec license when I get IDed in bars and go through through the whole apology that my birthday is in fact in french. Bouncers love that.</li>
<li>Speaking of, I’ve misplaced my Quebec licence and that’s very irritating.</li>
<li>Euro 2020 is on! As per, my heart team is the Netherlands and they’re
doing surprisingly well! They’ve been #blessed with a pretty easy group (rip group F) and I am not
looking that gift horse in the mouth.</li>
<li>It’s a month that ends in y so I’m trying to redo my site. Inspos: I really like how clean <a href="https://thefox.is/about">Karolina’s</a> is; I’m low key of obsessed with this <a href="http://yearinreview.creativedestructionlab.com/#">absurd cursor</a> from CDL.</li>
<li>There’s a bear in Tahoe that I’m 100% convinced is just trolling our dog. He spent a whole day casually
pacing up and down next to our house, which sends Hopper into a fit of barking hysteria.</li>
<li>Books: I finished Midnight Library and really didn’t like it, and Convenience Store Woman and it reminded me a lot of absurd theatre plays. I’m starting Klara and the Sun now.</li>
</ul>
Week 22: a week of sports2021-05-31T00:00:00+00:00https://meowni.ca/posts/week-22<ul>
<li>Trying out titles because I have enough weeks that even I can’t tell them apart and I lived through them.</li>
<li>Sports!!! We went to the Nations League ⚽️ semis and finals in Denver. If you don’t know this about me, I am a bit of a football (soccer) hooligan. Zach and I go to games, we sit in suporter sections, we chant. We went to France for the Women’s World Cup, because sports is a great excuse to travel, and the US women’s team is rad af. On top of that, a weird goal of mine is to be one of the drummers doing the chant drum beats, so I was super happy I got to <a href="https://www.instagram.com/p/CP9CDo_pBOw/">drum a couple of chants</a> at both games! I even got a gnarly workplace injury blister this time! I think the next big international game is in Vegas, and this might be the one where I try to bring my very own snare drum? Stay tuned.</li>
<li>Colorado in general has given up on masks for the fully vaccinated, which is still so very strange to me. I ate indoors. I walked into a store without a mask on. I hugged friends I haven’t seen in a year. I hugged new friends I met in a bar.</li>
<li>We had 2 free days in between the semis and the final, so we went to Boulder! I hadn’t been before and I LOVED it. It’s so cute and chill. I would definitely be happy living there for a couple of years.</li>
<li>Books: I finished The Lies of Locke Lamora. It was an enjoyable story of thievery and shenanigans. I’m reading Midnight Library now and I am a bit worried because Laura (with who I usually agree on books) said she thought it was badly written, and I am a bit of a writing snob (eg I genuinely loved the writing style of The Starless Sea and a lot of people find that book unbearable).</li>
<li>I have updated my LinkedIn for the first time in over a decade. What a weird place that site is.</li>
</ul>
Week 212021-05-25T00:00:00+00:00https://meowni.ca/posts/week-21<ul>
<li>Chopped off 10 inches of my hair and I feel unstoppable. I always <em>think</em> I can deal with mermaid hair when the reality is I cba to brush it and it keeps getting longer and drier until I’m ready to take the kitchen scissors to it. I resisted that impulse and got a professional to cut it. Am I finally an adult?</li>
<li>I am writing this update FROM A PLANE. Yes, a real plane. My brother-in-law’s family is moving into a new house and we’re going to celebrate. Also I haven’t seen their kids in a year and a half, which is 50% of one of them’s lives. She talks now! What!</li>
<li>Update: the inner life of 3 year olds is outstanding. We are just guests in their world.</li>
<li>Books: I finished the Vanishing Half and it was Very Good. I mostly avoided reading it because it has the most 2020 book style cover I’ve ever seen, but it was quite good despite that. Some weeks back I also finished On earth We’re Briefly Gorgeous and it was a bit of a miss for me. I like everything it was about (love a good immigrant memoir) but I didn’t enjoy the writing, which made the actual reading time a bit of a slog. I’m now reading The Lies of Locke Lamora because TikTok told me about it.</li>
<li>Jake did <a href="https://twitter.com/jaffathecake/status/1398519620764581888?s=20">a lot of walking</a> for cancer (he’s against). He walked 100km in a row, in something like over 24h. As part of it he also did an hourly ama (or at least until he became a delirious shell of a man), and Frances and I (henceforth known as the <a href="https://twitter.com/jaffathecake/status/1398765439770107904?s=20">chuckle sisters</a>) covered 6 shifts. It was good fun and I’m enormously proud of him.</li>
<li>One of the best things about having a FitBit smart watch instead of an Apple one is that I can make my own faces really easily (in JavaScript!!!). I wanted something that looked very clean on the main screen but also had the option for a stats heavy second screen, and I could never find quite the right one so I made my own. Putting this work experience to good use!</li>
</ul>
Week 202021-05-17T00:00:00+00:00https://meowni.ca/posts/week-20<ul>
<li>normalcy resumes chez nous. I know this isn’t true everywhere, so if you’re in a place of lockdown: I am sorry for how annoying these updates will be. I was a good girl and bunkered down for over a year, and now I’ve got antibodies in spades and taking advantage of it.</li>
<li>first trip of the after times: Monterey! I’d never been, because prior to last year we didn’t have a car, and renting a car for a 2 hour trip always felt daunting. However, now we have a car, and San Francisco is full of lovely things just a drive away.</li>
<li>aquarium is still on pandemic rules so I didn’t get to see any 🦦🦦🦦. Next time!</li>
<li>left Hopper at a dog boarding place for half a day so that we could have brunch in Carmel “without the kids”. First, paying for eggs I didn’t cook was an incredible experience and I almost cried (this is a theme this month, every time I do something that was pedestrian and normal in the before times I get overwhelmed with emotion). Second, Hopper a) fooled the humans into somehow trusting she’s not a vacuum cleaner and promptly ate another dog’s lunch and b) was a squirelly weirdo and mostly wanted to hang out around humans and not the other dogs, which is 60% her personality and 40% something she picked up in the apocalypse.</li>
<li>had an <em>indoor</em> fancy dinner with fellow vaccinated friends, to celebrate our new superpowers, and the place had white Russian ice cream floats for desert and can I just say: yes.</li>
<li>EUROVISION. Incredible showings from Iceland and Ukraine, and an absolute shit bouquet when it came to voting. Collusion has been a Eurovision classic since the dawn of time, but this year it was enraging. France, really? Discount Edith Piaf? What a travesty. Special mention however to the international conspiracy of letting the UK go to the final only to award them 0 points across the board. A masterpiece; well done everyone.</li>
</ul>
Week 182021-05-03T00:00:00+00:00https://meowni.ca/posts/week-18<ul>
<li>
<p>Ya look at that? It’s May already. My dog’s is turning 2 on the 14th, does anyone have any
good dog cake recipes or should I just give her an almost-empty jar of peanut butter with the same
success rate?</p>
</li>
<li>
<p>I got my second fouchie outchie! (#teampfizer). I wouldn’t say it bodied me the next day,
but I felt uncomfortable enough to sit in bed and read not one but TWO Bridgerton novels. I’ve
now read 1-4 and I think I’m done for a while.</p>
</li>
<li>
<p>Speaking of books, I’m finishing up On Earth We’re Briefly Gorgeous (Ocean Vuong; reads like poetry
but isn’t. Review next time), and I’m about to start up The Vanishing Half (Brit Bennett; very 2021 book cover). I also finished a book in Romanian, translated from Russian: Zuleiha opens her eyes (Guzel Yakhina. Really beautiful, much more hopeful and uplifting than the Russian literature I’m used to.)</p>
</li>
<li>
<p>After 15+ years of living in North America, I think I am “into” oatmeal. Oatmeal isn’t a Romanian thing
at all – oats are for horses, cream of wheat is for babies, adults don’t eat either. Current
approach: 1. overnight oats (in water, cashew milk makes them too…intense and I don’t give money
to the meat industry for cow milk) + either apple sauce (that I make because I keep forgetting to eat apples), or frozen fruit.</p>
</li>
<li>
<p>Other food shit I’m obsessed with right now: ingesting industrial quantities of watermelon, these
<a href="https://gimmedelicious.com/keto-peanut-butter-cookies/">keto peanut butter cookies</a> (I don’t eat keto,
but pb cookies with no-guilt-sugar are delicius), cashew milk.</p>
</li>
<li>
<p>Arts: I am deeply struggling with a 3 layer linocut. I’ve carved it like 3+ times, and no matter how I do it I can’t line up all the 3 layers well. I think it’s time to give up on it and make it into a digital print. Also, I’ve been working on L-systems, which make pretty trees. Should I write a blog post about that when it’s done?</p>
</li>
</ul>
From JavaScript to paper: a linocut adventure2021-04-28T00:00:00+00:00https://meowni.ca/posts/tree-rings<style>
.floatie-bit {
display: flex;
flex-direction: row;
}
.floatie-bit > p { margin: auto}
.floatie-bit > img {
width: 35%;
flex-shrink: 0;
vertical-align: middle;
border-radius: 10px;
}
.floatie-bit > .img-column {
width: 30%;
flex-shrink: 0;
display: flex;
flex-direction: column;
margin-right: var(--spacing-s);
}
.img-column img {
width: 90%;
vertical-align: middle;
border-radius: 10px;
}
@media (max-width: 700px) {
.floatie-bit {
flex-direction: column-reverse;
}
.floatie-bit > img {
width: 80%;
}
.floatie-bit > .img-column {
flex-direction: column;
width: 100%;
margin-top: var(--spacing-s);
align-items: center;
}
.floatie-bit > .img-column > img {
width: 80%;
padding-top: var(--spacing-s);
}
}
</style>
<p>One of my favourite kinds of art to make involves taking nature and seeing it as simple shapes. Buildings are cubes, flowers are circles, hills are curves. Shells are spirals. Tree rings are weird circle bois, and they are some of the best. I’ve wanted to make a generative art of a tree ring for a long time, but everything I made kept sucking (scroll to the bottom if you don’t believe me. Shit was bad bad). I finally made something I like, I thought it might be neat to write a little bit about The Process™️, since it involves both JavaScript and murderous little knives.</p>
<h1 id="lets-talk-trees">Let’s talk trees</h1>
<p>My friend <a href="http://twitter.com/kneath">Kyle</a> has a ranch, and in the summer he lets his friends fulfill the burned out millenial dream of having no cell signal and sleeping in a tent. He has infinitely many trees, and sometimes he lets me do a craft with some of the trees he chops down. I’m “currently” (read: I started last year and I’ll probably finish in 2026) making a side table. I also made these coasters:</p>
<p><img alt="a photo of a small section of a tree ring" src="/images/treerings/coasters.webp" /></p>
<p>Trees are rad, yo. Young trees have very regular and evenly spaced rings because much like human children, they haven’t had a lot of life to live. However, as trees get older, they get jobs they don’t like, have to start making dinner every night, start having back pains from literally just being trees and their rings get wonkier. Here is what I’m making my table from:</p>
<p><img alt="a photo of a bigger section of a tree ring" src="/images//treerings/table.webp" /></p>
<p>Trees can grow unevenly because of droughts or insects or capitalism building shit where it shouldn’t. They get visible scars from forest fires. You can map the entire life of a tree (and tbh, of everything that was happening around the tree) by looking at its cross section. There’s something called a <a href="https://oceanservice.noaa.gov/facts/ghost-forest.html">ghost forest</a> that indicates at some point, something happened (like an earthquake), which caused salt water to rise, killing all the trees that lived there. Here’s a photo (<a href="https://www.thechiefnews.com/news/tree-rings-tell-physiology-of-drought-intolerance-from-fire-suppression/article_15bdab20-096d-11e9-a7d2-6b0dc38e5dc2.html">ref</a>) of a knotty tree (heh heh) that has fire scars:</p>
<p><img alt="a photo of a section of a tree ring that is very elongated and has a lot of dark blobby spots, that are labelled with years." src="/images//treerings/fire.webp" /></p>
<p>So you know, if you want a tree ring, you can’t just draw some concentric circles and call it a day.</p>
<h1 id="generate-some-tree-rings">Generate some tree rings</h1>
<p>Montage time: the Rocky-runs-up-the-stairs song is playing in the background. I am
hunched over a laptop because I have the posture of a pretzel and the willpower to change that of a golden retriever left alone with a burrito. I am furiously typing for-loops in an editor; my cat is trying to drink from my water glass even though there’s a perfectly nice pet bowl of water next to it. I save these files to my computer to document what I think is going to be a cool progression of generated art. It is, in fact, not.</p>
<p><br /></p>
<div class="floatie-bit">
<img alt="some close concentric circles, each with jagged edges" src="/images//treerings/1.webp" />
<p>I drew some concentric circles and tried to call it a day. They looked stupid. The circles are kind
of jaggedy because I didn't even try to use Perlin noise, that's how lazy I was. If you don't know about Perlin noise, <a href="https://varun.ca/noise/">Varun's article</a> is 😘🤌 and fun.</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="concentric rings of small strokes" src="/images//treerings/2.webp" />
<p>I tried to throw away the circles and just paint their shapes with strokes. This certainly looks interesting, but it doesn't do tree rings justice. Don't worry: I'll end up using these painty-strokes in a different project.</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="close concentric circles, some of which are overlapped by randomly positioned small strokes" src="/images//treerings/3.webp" />
<p>"What if you keep the rings AND the strokes but you randomize them more".
<br /><br />
No.</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="small concentric circles like in the first image, but with some black triangles" src="/images//treerings/4.webp" />
<p>"Maybe go back to the first circles and add some cracks. Maybe that's what's missing".
<br /><br />
Narrator: it was not. So this definitely looks like a tree ring! But it also 100% looks like a for-loop generated it and that is not a thing I'm sticking my name on.</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="smoother concentric circles, of different thicknesses and greyscale colours, with some black lines overlapping" src="/images//treerings/5.webp" />
<p>"Perlin noise. Didn't I say Perlin noise fixes everything? This will fix everything".
<br /><br />
Better, but it still looks like someone in CS-101 drew it, let's be honest.</p>
</div>
<hr />
<div class="floatie-bit">
<img alt="smoother concentric circles, of different thicknesses and greyscale colours, with some black lines overlapping. they are pretty regular towards the center, but much wobblier towards the outermost circle" src="/images//treerings/6.webp" />
<p>Play with the noise parameter, making the outer rings noisier than the inner ones. Don't
play with it too much though or it will steer rapidly into drippy Dali clock territory.
<br /><br />
This was okay. The shape looked like a believable tree ring; it was just kind of...boring.</p>
</div>
<hr />
<p>This, dear reader, is where I realised this wasn’t going to end well, and here’s why: after all these
explorations I had a fairly good idea of what I wanted the tree ring to look like, but I didn’t
know how to say it in JavaScript. All of this is drawing pixels and curves in the html canvas, and that shit is hard. I have no idea how to start adding textures and stuff to it, and the point is:
I don’t want to learn. For me, that isn’t what generative art is about, that’s what traditional mediums are about.</p>
<h1 id="carve-em-up">Carve ‘em up</h1>
<p>In parallel, I also started making <a href="https://en.wikipedia.org/wiki/Linocut">linocuts</a>. Linocuts are a lot like stamps – you carve the mirror image of what you want to see, usually into a piece of linoleum using a sharp v-shaped knife. With stamps you press the stamp into an ink pad; with linocuts you roll the ink onto the block, and press the paper onto the inked block to make the impression.</p>
<p>For context, the scene is: March 2021, in the middle of the pandemic. I am bored senseless, and stabbing squishy rubber with a sharp knife sounds like a great way to spend a Saturday. Also, there’s something poetic about literally bleeding for your art because as
a very clumsy person you best believe I accidentally (and sadly more than once) jammed a knife in my thumb.</p>
<div class="floatie-bit">
<div class="img-column">
<br />
<img alt="a printout of a wobbly set of tree rings" src="/images//treerings/7.webp" />
</div>
<p>I started with this generated tree ring. I don't have the digital file anymore because this part went so poorly and made me so mad I probably threw it out after a tantrum. I drew some random lines to "represent texture" on the printout and went in with the misplaced attitude of "I'll figure the rest when I carve it", because despite this never working for the last 35 years of my life, it's still something I insist on trying.</p>
</div>
<hr />
<div class="floatie-bit">
<div class="img-column">
<img alt="a really bad and messy print of alleged tree rings" src="/images//treerings/8.webp" />
<img alt="an even worse and messy print of alleged tree rings" src="/images//treerings/9.webp" />
</div>
<p>
The linocuts and the prints were a mess, and here are some samples. I can't stress this enough, linocutting is not a medium for improvisation. I tried this chaos approach THREE more times,
as if time was going to help (it didn't). It was truly bad. I'm only showing you this to make you feel better about whatever projects you have in progress and don't feel great about. </p>
</div>
<hr />
<div class="floatie-bit">
<div class="img-column">
<img alt="a photo of a bigger section of a tree ring" src="/images//treerings/11.webp" />
<img alt="a photo of a bigger section of a tree ring" src="/images//treerings/12.webp" />
</div>
<p>I finally took a step back and made a plan, like I should've done in the first place. I imported
the generated tree ring in Procreate, drew a bunch of other lines and cracks on it until it looked right.
At this point it looks *very* different than what we started with, but the foundations are all there: the edges and rings have the same shapes, the cracks are mostly in the same places.</p>
</div>
<hr />
<p>What a ride, eh? I’m happy that I did all the generative explorations, because now I have a PILE of JavaScript
I can just pull up whenever I need to: noisy circles, paint strokes, blobby stroke lines. I am also happy that
I ended up finishing this as a linocut: it now feels like the human-for-loop collaboration of my dreams.</p>
<p>I’ve put up these prints for sale in <a href="https://www.meownica.studio/product/tree-rings">my store</a> – they are all hand printed by me, either using black ink on white deckle paper, or with gold ink on a black paper.</p>
<div class="floatie-bit">
<img alt="a photo of a bigger section of a tree ring" src="/images//treerings/final1.webp" />
<br />
<img alt="a photo of a bigger section of a tree ring" src="/images//treerings/final2.webp" />
</div>
Week 172021-04-26T00:00:00+00:00https://meowni.ca/posts/week-17<ul>
<li>
<p>I’ve been skipping weeks because a) literally nothing happens and b) I don’t have a good
system to update these notes. They’re a markdown file on a GitHub repo, and I kind of
need a computer to edit it, but I also kind of don’t open my computer that much these days?
I also keep forgetting which day is Monday. Room for improvement.</p>
</li>
<li>
<p>I wrote a <a href="https://meowni.ca/posts/tree-rings/">blog post</a> about how I generated some
tree rings in JavaScript and then carved them as a linocut. It doesn’t actually contain any
JavaScript, but it does have a lot of pretty images.</p>
</li>
<li>
<p>The last normal thing I did before the panini started was go to Japantown and stock
up on apocalypse supplies (snackos, mucho ramen, milk tea powder, korean face things).
The first normal thing I did with my 1 vaccine shot was go to Japantown and restock
all the things. This wasn’t on purpose, but I am pleased with the serendipity. It is cherry blossom season, so it was very pretty, HOWEVER, some racist asshole vandalized two of the oldest cherry trees there in January. Literally chopped down all of the branches, one at a time. What the actual fuck.</p>
</li>
<li>
<p>If you’re against plastic (why aren’t you?) and use deodorants, Dove has started selling <a href="https://www.dove.com/us/en/deodorants/refillable-deodorant.html">refillable</a> ones. I got mine from Target. The refill itself
still comes in plastic, but overall it’s far less plastic than the obnoxious amount the normal ones have. I’m excited about this not because this deodorant is particularly amazing, but because Dove is a HUGE brand, and
having mainstream brands start looking into more reusable, less-plastic products is a small but exciting progress.</p>
</li>
<li>
<p>Speaking of waste, Zach bought <s>us</s> me the most amazing thing: a <a href="https://www.vitamix.com/us/en_us/shop/foodcycler-fc-50">foodcycler</a>!!! We’ve been composting for years, but I’ve recently started getting lazier about putting the compost back in the freezer when I’m done with it, so our idiot dog has been stealing a lot of compost (which is full of bad things for her like coffee grounds, onion peels, literally half a spaghetti squash rind she ate and threw up for 8 hours). This foodcycler thing takes the compost and dehydrates it and grinds it and in 4 hours is done and gives you back fertilizer at like a tenth of the original volume. We’ve had it for a week and it’s honestly THE MOST innovation I’ve seen in my kitchen.</p>
</li>
</ul>
Week 152021-04-12T00:00:00+00:00https://meowni.ca/posts/week-15<ul>
<li>
<p>First week of being funemployed! It was very weird. By Wednesday I had lost track of what
day it was, and I kept thinking it was a Saturday. By Friday I was overwhelmed with capitalism
guilt and thinking that I HAD to do something to “stay productive”. What a fake idea y’all; it hadn’t
even been a full week of vacation!</p>
</li>
<li>
<p>I got vaccinated!!! I had no side effects other than a very sore arm, which is strange because
my immune system is the golden retriever of immune systems and doesn’t miss a free opportunity
to throw a fever on something.</p>
</li>
<li>
<p>Games update: Played “Cozy Grove” (comes on many platforms. I played it on my phone but
it’s also on the switch. It’a cute Animal Crossing like island game with daily tasks), and “Dear Reader”
(I played it on my iPhone. It’s a very cute literary game where you “read” a book by unscrambling
text puzzles in sentences. I’m super curious about how they summarize the book, because it feels very well done. I “read” Flatland like that, and you totally get the gist of the whole story while obviously
not reading the whole book. Pretty neat)</p>
</li>
<li>
<p>I was on a <a href="https://theworkitem.com/blog/creativity-monica-dinculescu/">podcast</a>, and it was proper
fun. Dan is a really nice and thoughtful interviewer. I was joking with him that I’ve been a bit
traumatized by podcasts since this weird show I was on a while back about Web Components, where the
hosts wanted to just produce drama and stir shit; I think they even opened up with something like “this
could be the Jerry Springer of JavaScript” and I was like……am I the pregnant 16 year old without
a baby daddy in this story because I think I should be offended. Anyway. This podcast wasn’t anything
like that, and I thoroughly loved being on it.</p>
</li>
<li>
<p>Also my hair looked great.</p>
</li>
<li>
<p>I got to the Big Reveal on the Mentalist and I gotta say: it was very good. Unsual for a procedural show
to have like a very strong arc, but this was a thing that was 5 seasons in the making and it was great. Unfortunately this means I am reaching the end of this show and I don’t have a next procedural lined up!!!</p>
</li>
<li>
<p>Dog has an ear infection and it’s incredibly gross. Golden Retrievers have a lot of ear.</p>
</li>
<li>
<p>I think the trick to “exercising” is just living in yoga pants and sports bras. I am 100% more likely to
exercise if I’m already dressed for it, because the thought of changing into yoga pants from whatever potato sack outfit I’m wearing just to go exercise seems daunting. Is this a life hack? Also, what is wrong with my brain that changing into different clothes seems like an unmeasurable amount of effort?</p>
</li>
<li>
<p>Actually don’t answer that.</p>
</li>
</ul>
Week 142021-04-05T00:00:00+00:00https://meowni.ca/posts/week-14<ul>
<li>
<p>Let’s not bury the lede: I quit my job. After 8 years of working there, Google feels like a very different
company than the one I joined, one that aligns less and less with my values, and it was time to move on.
It sucks, because my immediate team was a group of wonderful people who do great work; I will miss them,
and the work I did, greatly. I haven’t had a vacation longer than 2 weeks since I was 18-ish, because I worked every summer and was too much of a naive workaholic to think I should take more than 1 week between jobs, so I am giving myself 6 months before I start something new this time around. It’s stressful to not have a plan, and I am bad at relaxing, but I have full confidence I can learn to overachieve at this as well.</p>
</li>
<li>
<p>Didn’t I tell y’all I was worried SOMETHING was going to happen and I wasn’t going to get my vaccine? Here I am, 3 hours before my J+J stabbie stabby, reading about how my appointment is cancelled because it has an incredibly rare clotting side effect. One that is 10000 more rare than the usual clots women get threatened with for taking birth control and that absolutely nobody blinks an eye at anymore. This feels a lot like fabricated concern; we already don’t care about women getting blood clots. 1 in over 6 million? Fam, that’s better odds than getting in a car or not getting e-coli from Chipotle. Stop this.</p>
</li>
<li>
<p>I’ve become very bad at telling stories, which is 100% a result of the panini and not being forced to socialize with people. I’ve noticed several times in recent weeks where I’m telling a story and can see the life leaving from people’s eyes. I am bored of what I am saying as I am saying it. I am not ready to move to the midwest yet; I must fix this.</p>
</li>
<li>
<p><a href="https://twitter.com/Danez_Smif/status/1381449712428060672?s=20">This</a> is a very good tweet. Abolish the police already. Stop giving guns to insecure people who think a uniform gives them the right to kill others because of the colour of their skin. Stop giving guns to incompetent people who apparently aren’t trained enough to tell the difference between a gun and a taser, but are confident they have a right to use either. Stop giving guns to people.</p>
</li>
<li>
<p>I am working on 2 linocuts. I should probably start working on more generative stuff, since JavaScript is my bread and butter and should somehow prove to my future employers I still got it. Or something. I am a deflated balloon after finding out my vaccine was cancelled and writing about yet another dead black man above so I don’t really know how to end this update on a good note.</p>
</li>
</ul>
Week 132021-03-29T00:00:00+00:00https://meowni.ca/posts/week-13<ul>
<li>
<p><a href="https://www.nintendo.com/games/detail/calico-switch/">Calico</a> came out and I finished it in 2 days! It’s an adorably cute game about running a pet cafe in a fantastical little world. It’s honestly the kind of game I would make. It has an incredible cooking activity interaction, which I can only describe as “shit, this game engine has physics and collision detection, let’s use it for something!”. It’s great. (spoilers if you don’t want to play it: the interaction is “you turn into a smol person and throw the cooking ingredients that are now bigger than you at a giant bowl”. it’s wonderful.)</p>
</li>
<li>
<p>I am now playing Fenyx Rising on the Switch because it looked like a Zelda Breath of the Wild clone and can confirm: it’s basically BOTW minus cooking plus quality of life improvements (swords don’t break! more looting and outfits!) and sassy greek gods. Don’t know how the Ubisoft lawyers managed to sweep that under the rug, but I for one welcome the 50+ hours of gameplay ahead of me.</p>
</li>
<li>
<p>I tried to make a Queen of Puddings desert because I saw it on celebrity bakeoff and like Dizzee Rascal (self proclaimed literal first time baker) managed to follow the recipe and do it so I felt overconfident. My custard didn’t set; I would’ve come last in the technical challenge. Also, it was WAY too sweet in my opinion, but at least that part wasn’t my fault.</p>
</li>
<li>
<p>I also made my own homemade kaya! I discovered kaya when I was in Singapore 2 years ago, and it was life changing. A kaya toast set is probably my favourite breakfast concept, and I missed having it since I ran out of the Ya Kun kaya I bought on that trip. Worry no more: I am back on that bullshit.</p>
</li>
<li>
<p>I am doing a 2 week <a href="https://www.chloeting.com/program/2020/two-weeks-shred-challenge.html">Chloe Ting program</a>, which means I have exercised every day this week. This is a huge record that I am very proud of, because I think exercising is the most boring and awful activity in the universe. I hated every minute of it, but by Sunday the minutes SEEM to at least pass faster? I dunno fam, it’s not great.</p>
</li>
<li>
<p>I’ve started cross stitching while watching the Mentalist. It feels…oddly relaxing? I have no idea what I’m going to do with the result of cross stitching though, hang it in a bathroom somewhere? Is it too early to go down the pensioner aesthetic route?</p>
</li>
<li>
<p>The linocut print of generated-with-javascript tree ring is almost done! I’ve been making some test prints, and they’re cute! I think I’m going to write a blog post about the process because I am very happy with it!</p>
</li>
<li>
<p>I think summer is coming. Better get that SPF out.</p>
</li>
</ul>
Week 122021-03-22T00:00:00+00:00https://meowni.ca/posts/week-12<ul>
<li>
<p>Trying something really revolutionary this week which is “doing nothing”. Bear with me here. I mean: listening to an audio book and NOT also cleaning the house. Just sitting, listening to that book and doing nothing. WASTING THAT TIME. In this economy??? Yes. Do I dislike the idea? Also yes.</p>
</li>
<li>
<p>Related: I switched to the Samantha Irby (Wow, no thank you) audiobook because a) she narrates it and her voice is great and b) after 365+ days in the panini, a voice that isn’t my husband’s is a welcomed blessing.</p>
</li>
<li>
<p>I shipped out 10 linocut prints and (hyperbole) it was the most stressful thing I’ve done. Hand prints
are beautiful because they’re so textured and imperfect and really truly awful because they’re so textured and imperfect. I basically printed 10 extras so I’d have something to pick from, and ended up hating all of them. I hope that’s just my brain, and the people who they went to end up liking them.</p>
</li>
<li>
<p>California is opening up vaccines to all humans after April 15, and I have a vaccine appointment for April 22. It’s been so long since this started that I’ve lost all hope. I assume something horrible will happen like they’ll change their minds or I’ll show up and they’ll be like ha ha no vaccines for legal aliens or people whose names start with the letter M or they’ll run out or I’ll sleep through the appointment or or or. Welcome to my brain, it is a literal prison.</p>
</li>
<li>
<p>All week I did many linocuts and no JavaScripts, so next week I’m making a rule to either only carve
generated things, or only generate things. In the carving pipeline: a generated bookcase, a generated tree ring. In the coding pipeline: a techno landscape (maybe???) for my friend Daniel, some posters for a conference my friend Jana is organizing. Neither of them read this and that’s for the best.</p>
</li>
<li>
<p>I <a href="https://www.instagram.com/p/CMm6WeKHgVM/">bought an art</a> from an artist on Instagram! I don’t usually like very abstract art, but this one is so bright and happy and I think will look great in our very white-walls apartment. It’s coming from Malta, a country about which I know very little about (capital: Valetta, location: off the tip of the Italian boot, anything else: no)</p>
</li>
<li>
<p>Big week to be a boat, eh?</p>
</li>
</ul>
Week 112021-03-15T00:00:00+00:00https://meowni.ca/posts/week-11<ul>
<li>
<p>It’s been another week of people being shit, innit? I know I should be
used to this by now, but it never ceases to amaze me the capability people have for violence
and evil, and the capability the patriarchy has to excuse it. “Dude had a bad day”? For real real?
If that excuse has legs then you’d expect every uterus-holding person to do at LEAST a murder
per year. And yet it’s always white dudes who haven’t ever thrown up from cramps or had to deal with birth control hormones that feel they’re
entitled to attention or sex or whatever and go on these racist murder sprees. And they’re
going to continue to as long as the media excuses them as “bad days”, or gives them a platform by
encouraging racism and xenophobia. If you’re a Fox news anchor who’s ever said the
words “china virus” in the last year…these murders are on you pal. I see you, and I deeply hope you don’t have a good night of sleep for the rest of your life.</p>
</li>
<li>
<p>I took all this anger and donated to Red Canary Song and also pointlessly stabbed linoleum with it. The goal is to carve things a computer generates,
but at the moment that’s a bit too hard for me (I make the computer generate a lot of lines and uhhh that’s a lot of cutting), so I’ve been carving some non-generative stuff. They’re all a bit surrealist, because that is extremely my shit, but I don’t really know what to do with them.</p>
</li>
<li>
<p>I also don’t understand my audience. For example, I made this <a href="https://meownica.bigcartel.com/product/the-internet-and-plants">computer plant</a>. I thought it was meh, so I wasn’t gonna put it up in the store, but after tweeting about it, it ended up selling out in a nanosecond. I made this March Madness <a href="https://meownica.bigcartel.com/product/basketball-buzzers-beets">basketball beet</a> that’s
like…actually well carved and in two colours and cute and nobody cared. The same happened with the generative stuff – the <a href="https://meownica.bigcartel.com/product/smoke-1">chaos attractors</a> I thought were deeply boring sold out instantly, the <a href="https://meownica.bigcartel.com/product/flight-3">wings in flight</a> that
I absolutely adore are a no-op. I don’t get it, y’all.</p>
</li>
<li>
<p>I am back on my procedural bullshit. <a href="https://twitter.com/mrmrs_">Adam</a> has been telling me about
the Mentalist for months now, and holy shit: he was right. A++ procedural murders, be back in 7 seasons.</p>
</li>
<li>
<p>Every year I say I’m going to do a March Madness bracket where I ignore the chalk and just pick it on names and mascots, and every year I think the chalk knows best so I don’t and get busted on like day 1. I see you Oral Roberts toothpastes and Loyola wolf boys, I should’ve kept you going to the final 4 like I wanted to, eh?</p>
</li>
<li>
<p>Related: defensive fouls in basketball are absolutely bullshit.</p>
</li>
<li>
<p>Zach and I are rewatching all of the Marvel movies in story chronological order. This means that I went on
several really productive 4 am YouTube holes of watching Avengers bloopers, every recorded interview with Robert Downey Jr, and that whole Ally McBeal season he was on. Imagine if I could put that kind of energy in watching videos about quantum mechanics or something. Instead, I can tell you that he met his wife when filming Gothika, that Tom Hiddleston couldn’t stop laughing during that one scene in Avengers 1 when Hulk smashes him into the Stark tower apartment floor, and that Mark Ruffalo is scared of needles so he doesn’t have the Avengers tattoo everyone else got. WHY AM I THIS WAY.</p>
</li>
</ul>
Week 102021-03-08T00:00:00+00:00https://meowni.ca/posts/week-10<ul>
<li>
<p>I took the whole week off because I was very tired of life. It’s the year anniversary of the last
time the world was normal and it’s bringing me down. Between covid and immigration
delays it’s looking very 50/50 on whether I can attend my closest cousin’s wedding this fall in Romania,
and I’m beyond gutted at the thought of missing it. All I can do about it is wait, and that’s making the pandemic fatigue worse by a thousand.</p>
</li>
<li>
<p>I made two <a href="https://meownica.studio/">prints</a> of Barnsley ferns. They looked cool
in the end, but they were a bit of a struggle. Barsnley ferns are these super popular fractals
that (spoilers) look like ferns, but honestly, fractals in general are not my favourite. These
ones come out of 4 equations and 6 parameters which sounds like would open a whole world of
possibilities, but it turns out most combinations of values produce absolute trash. Which is fine,
but that’s not my style of generative art. Instead, I spent all my random numbers in trying to
paint the ferns like <a href="https://www.meownica.studio/product/ferns-single-edition">furry brush strokes</a> and <a href="https://www.meownica.studio/product/ode-to-barnsley-single-edition">letters</a> I don’t think anyone other than me appreciates that 😅.</p>
</li>
<li>
<p>I got deep into linocuts. I’ve been trying to figure out how to make the things that come out of a computer also come out of my hands, and hand prints can definitely be that thing. Doing the linocut itself is my favourite kind of relaxing; it keeps my hands busy with stabbing squishy things (also see: that time
i picked up felting in meetings). I can’t believe I haven’t tried it before. Also, Frances says that the linocuts I’ve been doing look a lot like the kind of carving I used to do with my pottery, which is great news because that means I have a *~style~*.</p>
</li>
<li>
<p>As usual, I am undeservedly lucky: one of my cousins, <a href="https://ralu.ca/">Raluca Iancu</a> is an enormously
talented printmaker and professor of printmaking, so I got to text her with questions. I don’t think
everyone who starts up with a hobby can text a professional in that area and be like “hello I bought 10$
tools and some linoleum, please hold my feelings with your 10+ years of domain expertise”.</p>
</li>
<li>
<p>Zach and I have been babysitting our friends’ smol puppy. Her name is Penny and she is SO CUTE. The cat
feels infinitely betrayed.</p>
</li>
<li>
<p>I am reading “Wow, no thank you” by Samantha Irby because she is hilarious and hates people almost
as much as I do. I just finished season 4 of Below Deck and I think I need to take a break because
I got way too invested in the romantic life of Ben the chef.</p>
</li>
<li>
<p>I am looking for a chill, weekly “draw this” prompt from instagram. If you’re reading this and you
know one that you’ve used or you like, please let me know!</p>
</li>
</ul>
Week 82021-02-27T00:00:00+00:00https://meowni.ca/posts/week-8<ul>
<li>
<p>This week was entirely consumed by the fact that I opened an <a href="https://meownica.studio">online store</a> to sell generative “art” prints. Art is in quotations here because I have a deeply unhealthy relationship with calling anything I do art, or myself an artist, but we don’t have time to for a therapy session right now.</p>
</li>
<li>
<p>It went really great. I sold out of 2 kinds of prints! People who weren’t just my friends put it orders! My first customer ever was Mariko, which is absurd because she should’ve gotten a free print without even asking based on the kilograms of gacha she’s brought me from Japan, but of course she was also the first one to see the tweet about the store because that’s just the
kind of friend she is.</p>
</li>
<li>
<p>(this paragraph is really niche and boring, skip it). Every once in a while I use a software that blows my fucking mind. E-commerce is generally NOT this experience btw, because it’s all integrations that are 90% magic and 10% wonky af. Like, my store is a BigCartel store (big fan!), but the site itself is a bit flaky so sometimes Things Will Happen™ (like pages won’t redirect, the theme editor will just panic, javascript is a prison). Stripe was like 95% magic and 5% “the oauth flow didn’t redirect well For Reasons™ so I had to do it twice”. I don’t even know who was at fault there. Anyway, PirateShip is a thing that prints USPS labels and it was 120% magic. People wrote their addresses in their orders in my store; I clicked a single button on the PirateShip site, which imported those addresses, made shipping labels, made customs forms for the international orders, formatted all the labels for my label printer to print AND emailed all the customers their tracking numbers. And charged me 0 extra dollars. PirateShip, I don’t know how you make your money but you are an absolute gem and I love you. The post office gentleman even commented on how together my shit was.</p>
</li>
<li>
<p>Frances and I had a FitBit active minutes challenge and I WON by like 32 minutes (aka extremely close). Zach was also
in some Apple Fitness challenge with a friend, so we had several “can we walk to the dog park, I need the points” moments.
The dog had the best week.</p>
</li>
<li>
<p>I am trying to get as good at painting on my iPad as I am in real life (which honestly, is not bad. I am a good technical
artist, I’m just basic and derivative, ya know?). I took some skillshare classes, and I installed a paperfeel screen protector
thing that really helped. The shinyness is so offputting for drawing.</p>
</li>
<li>
<p>I finished “Dept. of Speculation” by Jenny Offill. I really enjoyed it. I write book reviews on <a href="https://www.goodreads.com/user/show/27136484-monica">my goodreads</a> if you’re looking for a book to read.</p>
</li>
<li>
<p>I’m watching a lot of Below Deck (like, all the past seasons). I don’t know what this will do to my personality, but given that
it’s been almost a year of hanging out with pretty much just my pets and Zach, it probably won’t be an improvement.</p>
</li>
<li>
<p>Spotted: a very fuzzy bee smelling the flowers (mostly Nasturtiums it turns out) from the bee-friendly seeds I planted
last year. You know you love me, xoxo garden girl.</p>
</li>
</ul>
Week 72021-02-15T00:00:00+00:00https://meowni.ca/posts/week-7<ul>
<li>
<p>I got deep into geocaching, friends. I found 3 caches in Arizona! I didn’t sign any of them
because I kept forgetting to bring a pen, so now nobody will know I was there.</p>
</li>
<li>
<p>Geocaching is mad in the US. We drove back from Arizona (flying? in this economy??), and there were
HUNDREDS along the highway. First, who are these people that stop on the side of the highway to hide a cache?
Second, who are these people who stop on the side of the highway to go look for a cache? Were you raised by
wolves? Highways are dangerous yo, don’t just stop on the shoulder unless your car is on fire.</p>
</li>
<li>
<p>I am filled with fury about cryptoart. FILLED. I follow someone on Instagram who has raised money for the
national park service, and loves Joshua Tree, and a lot of her art is of nature, and she’s been minting like
3 NFTs a week. <a href="https://joanielemercier.com/the-problem-of-cryptoart/">Joanie Lemercier</a> wrote how 10 seconds of
cryptoart used more energy than their studio in 2 years. TWO YEARS. Imagine being an artist that is
minting cryptoart in the same week that Texas residents are bankrupt because they couldn’t pay their
$17,000 heating bill in the winter because the Texas utilities companies are messed up and unregulated and
can fuck with prices when supply is low. FILLED WITH FURY.</p>
</li>
<li>
<p>Related: I think part of the problem is that we’re not making people build those absurd rigs to mine crypto
anymore.In uni one of my friends built one of those babies to mine doge at home, and he could heat up his
house in the winter from it! Now you just sign up for a service that mines in a data center and heats up the
desert somewhere.</p>
</li>
<li>
<p>Also related: I 100% empathize with artists that selling art is hard and it’s impossible to make a living,
but as with everything, crypto isn’t gonna solve anything. Unless you have a weird perpetuity clause in your
NFT (is that a thing? I don’t know. I make it a point to know as little as possible about crypto because I already
have heartburn from regular life things and don’t need more), the only people making money from cryptoart are the people who
could already sell regular art for a shit ton of money. Otherwise nobody is going to pay any real money
for your anonymous neon animated block, crypto or not.</p>
</li>
<li>
<p>I tweeted about this AND I now wrote about it and I am still filled with fury. Sorry to the 2 people
who read these notes and also read this on Twitter. Nobody better email me about this; I cannot be convinced to not hate crypto, and I will just mute you with high prejudice and no guilt.</p>
</li>
<li>
<p>Honestly now that I am typing out these notes I’m also filled with fury about everything that’s
going on with Google and Research, so I don’t think I’ve done anything other than be angry all week. lol what
a time to be alive.</p>
</li>
<li>
<p>In the spare rage free seconds I’ve had left, I have been hard at work at getting meownica studio up and running. I have a special <a href="https://www.instagram.com/meownica.studio/">instagram</a> account. I took the <a href="https://www.meownica.studio/">actual store</a> out of maintenance mode. I enlisted my photographer friend <a href="https://www.ashleybatz.com/">Ashley</a> (she is enormously talented, please hire her) to take photos so that I can update the store with real life product photos. This week is the week, I feel it.</p>
</li>
<li>
<p>Also <a href="https://github.com/mattdesl/canvas-sketch">canvas-sketch</a> is dope and it does dope things (like use units that make sense)</p>
</li>
</ul>
Week 62021-02-08T00:00:00+00:00https://meowni.ca/posts/week-6<ul>
<li>
<p>I skipped week 5 because all I did in the panna cotta was watch 3 seasons of
“how to get away with murder”. It’s a terrible show and I’m not ashamed of it.</p>
</li>
<li>
<p>What is up with that star codex thing? I can’t believe there was a cult of
assholes in tech I didn’t know about. I’m obviously not surprised they existed,
there’s an asshole under every rock in tech; I’m just surprised I didn’t know
about it. I’m a person in the know! I have an invite to Dispo!</p>
</li>
<li>
<p>I watched the #freebritney documentary and I think that a) the concept of a conservatorship for a grown ass 40 year old woman is fucked and b) her dad being
a mysoginistic asshole is 80% of the reason she’s on one. Nobody
put Kanye in a corner when he was going bonkers; we let him run for
president and bought his shitty 20 minute long album to make fun of it and talked about how he prolly needs to take his meds but oh well,
famous people amirite. But if a woman goes a bit bonkers and in the process
shaves her head (we all know women aren’t allowed to shave their heads), then suddenly she needs to be in a legal monetary prison for life? Rich people waste their
money ALL the time, why is she the different one? Makes you think.</p>
</li>
<li>
<p>I also think it’s the cherry on the cake that because her dad is “managing” her
estate she is essentially paying both for her lawyers to try to help her,
and for the opposing lawyers to try to screw her.</p>
</li>
<li>
<p>I’ve been working on a lot of generative art. This is good, because I’m making
things and I want to open a store so people can put my things on their walls,
but bad because when I make art and keep looking at it I end up violently hating it, and that spirals out of control into a “everything I do is basic and derivative” mood which tbh makes me feel like shit. I had a nice chat with @mrmrs who is 100x more talented than me but feels similarly about his art sometimes, so I felt very seen.</p>
</li>
<li>
<p>We drove to Arizona this weekend. We are still doing nothing but now it’s
nothing around cactuses and the desert. I’ve been trying to find ways to get myself
to move more, and I read in Alice’s notes that she tried out geocaching, so I installed an app last night! I thought there would be like a geocache per state, but there’s millions. There are 2 geocaches within a 2 mile walk! If there’s no week 7 notes it’s
either because nothing happened or because I died trying to get to this cache. The halting
problem is a hell of a thing.</p>
</li>
</ul>
Week 42021-01-25T00:00:00+00:00https://meowni.ca/posts/week-4<ul>
<li>
<p>Tahoe (where we currently live during this pannie-d) got a ton of snow this week, so I’ve been in winter mode. Went skiing on Friday, went snowshoeing on Sunday. My calves are on fire. Skiing is pretty safe; everyone is wearing masks, they’ve closed all the
lodges, and tbh nobody should be within 6 feet of me skiing on a good day.</p>
</li>
<li>
<p>I can tell when I accidentally overdo it with the hobbies (which is every time I get excited about a new hobby) because I rapidly swing in the other
direction. I painted nothing this week, and I spent all weekend watching the entire season of Bling Empire in one day instead.</p>
</li>
<li>
<p>I knitted most of a
<a href="https://www.ravelry.com/patterns/library/quick-ombre-hat">hat</a>
and then unravelled it all because it had accumulated too many mistakes. It’s a really fun
knit so I’m not too bothered.</p>
</li>
<li>
<p>I spent a lot of evenings frantically working on a visualization about plastic waste (new hobby alert) that nobody asked for. It started as “lemme do a quick thing to get better at d3 and
at asking questions about the data” and it’s now a good 8+ hours of work in. Sure wish I would’ve half assed it, because now it’s not done and I don’t really want to finish it.</p>
</li>
<li>
<p>Still reading <a href="https://gretchenmcculloch.com/book/">“Because Internet”</a>; also started
<a href="https://www.amazon.com/Travelling-Cat-Chronicles-Hiro-Arikawa/dp/0451491335">The Travelling Cat Chronicles</a>.</p>
</li>
<li>
<p>I am really <a href="https://twitter.com/notwaldorf/status/1354298108083990528?s=20">irritated and confused</a> by this Hangouts/Chat situation. I am on an iPhone, and
want to send <a href="https://fberriman.com/">Frances</a> stupid links, hot takes and photos. We can’t use our respective
phone’s Messages app (because hers is over SMS which shits the bed on photos, and mine is Apple proprietary). We
were using Hangouts, but it’s deprecated, and wanted to use Chat, but it’s …
enterprise only and peons can’t start new conversations? Also Whatsapp is evil and
Signal spams all my contacts when I install it. What the fuck, I just want to talk
to non-Apple friends in 2021.</p>
</li>
<li>
<p>February is a perfect calendar <a href="https://twitter.com/standupmaths/status/1356212992669589504?s=20">rectangle</a> and that is satisfying.</p>
</li>
<li>
<p>Are week notes the new LiveJournal but without comments? Discuss.</p>
</li>
</ul>
Week 32021-01-18T00:00:00+00:00https://meowni.ca/posts/week-3<ul>
<li>
<p>Monday was off which ruined any routine I had formed. I barely got used
to waking up on Mondays to go to work and then bam, 2 weeks in, a Monday off. I had
dreams all week about working part time, probably because my brain is clinging
desperately to this hope of not having to ever wake up on Mondays.</p>
</li>
<li>
<p>I got 3 <a href="https://twitter.com/notwaldorf/status/1351609102330327043?s=20">aquatic snails</a> in a small little aquarium. They live next to my laptop so I can look at
them during the boring meetings, and they don’t seem to have any worries. Tentative names: Shelly, Shelmet, Tickle me Shelmo. They’re super active around noon, and I’m learning that whatever number of
tentacles I thought a snail had, it’s incorrect. I am obsessed with them.</p>
</li>
<li>
<p>We had an absolutely awful ER adventure with our dog. On Saturday night she was
really ill all of a sudden: she couldn’t stand without falling over,
couldn’t stop throwing up, and her nose was super hot (this is how you can
check if a dog has a fever!). Since she’s golden retriever and therefore a
trash vacuum, we were almost positive she ate a poisonous mushroom, or
some coffee beans or chocolate or something bad like that. Imagine our shock when
the ER did a tox screen and it came back positive for….MDMA. Best guess right now is
that someone at the airbnb next door (who hosts a lot of dubious frat boys)
threw something in our fenced-in yard. In any case,
dogs shouldn’t be on hard drugs, and she had a pretty rough rave of IV fluids and ice packs all night. Now I’m too freaked out to let her outside in the yard and have some empathy
towards the mothers who want to lock their childrens in the bedrooms and
never leave them out of their sight. What a shitty thing to do to a dog, man.</p>
</li>
<li>
<p>I am reading <a href="https://gretchenmcculloch.com/book/">“Because Internet”</a> by my second favourite linguist, Gretchen McCulloch.
My favourite linguist is Jane Solomon; she also wrote a book, <a href="https://www.amazon.com/Dictionary-Difficult-Words-perplexing-words/dp/1786038110">“The Dictionary of Difficult Words”</a>, and 7 year old me would’ve thought it was the absolutely best ever.
If you have a smart ass 7 year old, buy it for them.</p>
</li>
<li>
<p>I see some progress in my watercolours. Do’s: layer blobs of colours, but only
after the previous one has completely dried. Dont’s: No aggressive blending or
gradients that aren’t just “this colour bled into this other one”.</p>
</li>
</ul>
Week 22021-01-11T00:00:00+00:00https://meowni.ca/posts/week-2<ul>
<li>
<p>Still into soups: I made a bomb cream of broccoli. I also bought a new
<a href="https://www.orringtonfarms.com/product/orrington-farms-natural-vegan-chicken-flavored-base/">vegan broth base</a> to fuck with because buying the
cartons of broth is like buying bottled water aka: bad and wasteful.</p>
</li>
<li>
<p>My dog’s skin is turning grey. I know this sounds funny but it’s like, a thing. There could be a number of reasons for this, but the most likely one is that she’s an inbred dumbass and allergic to something in her food, like chicken. Or she could be dying. In any case, she’s now on Fancy Food™️ which makes her insufferable. Related: pet nutrition is absolutely bonkers.</p>
</li>
<li>
<p>If you’re trying to gauge what level of pandemic mental breakdown I’m at, I dyed
my hair pastel pink in the bathroom last week (Manic Panic blessing my life since 2001),
and this week I “rescued” a very sad looking croton from the grocery shop. I got
emotional that it was ugly and unloved and nobody else was going to care for it
because people only buy pretty plants, so I HAD to adopt it. I paid 3$ for this
privilege of bringing a plant with broken leaves into my house.</p>
</li>
<li>
<p>New season of drag race! I know this will be very polarizing, but I kind of love
how insane Utica is. I also love Gottmik (and that Ru has finally pivoted to saying
“and may the best drag queen win”) and Tamisha. Still don’t understand the pork chop
gimmick, and I haven’t eaten a pork chop in like a year and a half so it makes me hungry.</p>
</li>
<li>
<p>I had some really good meetings at work with super creative and inspiring
people, and it’s spilling into my personal life: I have 2 (TWO) ideas
for generative art projects, and 0 (ZERO) motivation to actually do them. I also
haven’t finished the Bach Menuet I started last week. What do I do instead with my
evenings you ask? Watch every single Trixie Mattel video on YouTube because that’s the journey I’m on.</p>
</li>
</ul>
Week 12021-01-04T00:00:00+00:00https://meowni.ca/posts/week-1<p>I’ve been thinking for a while about setting this up, and this week seems
as good as any. Both <a href="https://alicebartlett.co.uk/blog/weaknotes">Alice</a> and <a href="https://fberriman.com/blog/">Frances</a>
have weekly updates that are a joy to read. <a href="https://livelaugh.blog/about">Jenn</a> has a live laugh blog. Maybe this is the year of long form tweets?</p>
<p>I haven’t touched my site in like 6+ months so when I tried to run jekyll locally
it obviously didn’t work, so what you’re reading took like 40 commits on GitHub.</p>
<p>In work news, I’m coming back to Magenta after 6 months of being a fellow with the Trevor project. I lead a team that worked on classifying suicidal ideation for
one of their projects. The work was super interesting (though often sad), and in
a shocking development i really enjoyed being a team lead. I think that even though I’m
really good at writing actual code, I’m also good at herding cats and holding
a lot of statuses and details in my head. I was worried at the beginning of the
fellowship that TLs don’t write any code and I would suddenly become a meeting chair,
and even though I <em>did</em> have a ton of meetings, I also wrote a ton of code.
I know it’s weird to hear a woman assert things she’s good at it. This is also
a new thing I’m trying out. My notes, my rules!</p>
<p>In personal news, I’ve gotten really into broth-as-a-savoury-afternoon tea. I used to make a lot of miso (and drink it out of a cup; this part is key), but to add some
variety in my life, this week I started boiling a bunch of vegetables during meetings
and making my own broth. Anything for variety in the apocalypse, amirite.</p>
<p>Goldie moved in to my Animal Crossing island.</p>
<p>I am exclusively watching serial killer shows right now (Des, I’ll be gone in the dark, Ripper)
which isn’t exactly off brand but might be a little TOO on brand, you know? That’s what
I get for finishing Bridgerton in 2 days.</p>
<p>For my birthday in November my mum got me some new books of piano sheet music, so
I’ve started playing some piano again. Before I moved to America, I used to play every day and be actually good at it, but much like with everything else I got really burnt
out by the stress of “being good at a hobby” and started hating it. I think enough
time has passed that I can give it a try again, and maybe I won’t be so intense this time.
I’m currently trying to nail Bach’s Menuet in G. <a href="https://twitter.com/pcastr/status/1346654481451245569">Here</a> is my coworker playing around with it.</p>
<p>A lot of you JINXED this year like the jinxers you are because shit was looking
good on Tuesday, and lo and behold by Wednesday the world was on fire again and I
was sad and just watched the news in horror.</p>
Fixing typedoc's generated TOC if your code is using ES6 modules2020-02-21T00:00:00+00:00https://meowni.ca/posts/typedoc-toc<p>My one policy about blogging is “write the blog post you wanted to find in the search results”.
I spent an inordinate amount of time yesterday trying to get <code class="language-plaintext highlighter-rouge">typedoc</code> to only show the docs
for the files I’m actually exporting in my library, and didn’t find anything
on the internet to help me, so here is the blog post I wanted to read.</p>
<h2 id="the-problem">The problem</h2>
<p>You are working on a JS library. You author your source in TypeScript. You have
an <code class="language-plaintext highlighter-rouge">index.ts</code> file that exports only some of your source files. You would like
your generated docs from <code class="language-plaintext highlighter-rouge">typedoc</code> to only have docs for <em>those</em> files (Why? So
that people don’t open issues along the lines of “I see the docs for function <code class="language-plaintext highlighter-rouge">foo</code>,
but I can’t see how to call it, pls export it”. Sweet child, if that
function was meant to be public it probably would’ve been. That function is
actually 3 spiders in a trench coat).</p>
<p>That is, you would like your Table of Contents to show this:
<img width="500" alt="toc only shows 5 entries" src="https://user-images.githubusercontent.com/1369170/75011632-f396cb80-5434-11ea-97c7-708a94e932fe.png" /></p>
<p>and not this:
<img width="500" alt="toc shows every file under the sun" src="https://user-images.githubusercontent.com/1369170/75011626-f09bdb00-5434-11ea-859b-ab195c5f1b47.png" /></p>
<h2 id="things-that-arent-solutions">Things that aren’t solutions</h2>
<p>In the order that I’ve tried them:</p>
<ul>
<li>the <code class="language-plaintext highlighter-rouge">--mode modules</code> flag: the word “modules” is a lie here and really just means
“under a namespace” not like… ES6 modules (<a href="https://github.com/TypeStrong/typedoc/issues/109">tracking issue</a>)</li>
<li>the <code class="language-plaintext highlighter-rouge">--excludeNotExported</code> flag: it helps to generate docs for only the
exported <em>functions</em>, but not files</li>
<li>the <code class="language-plaintext highlighter-rouge">-excludePrivate</code> flag: same as above</li>
<li>the <code class="language-plaintext highlighter-rouge">--exclude</code> flag: this is nice in theory, but I have like 30+ private
files that shouldn’t be documented and only like 5 top level exports, so that regex
will suck. Also, my experience is that next time someone adds a file they want or
don’t want documented they won’t know to add it to this list and we’re back to having a problem</li>
<li>the <code class="language-plaintext highlighter-rouge">--toc</code> flag. I honestly don’t know what it does, but for me, it did nothing 100% of the time</li>
<li>thinking this should presently work in <code class="language-plaintext highlighter-rouge">typedoc</code>. Here is the <a href="https://github.com/TypeStrong/typedoc/issues/639">tracking issue</a>
and the <a href="https://github.com/TypeStrong/typedoc/pull/1184">open PR</a> that might fix it.</li>
</ul>
<h2 id="my-solution">My “solution”</h2>
<p>I’m less bothered that the docs for the private files get generated at all,
and more bothered that they’re linked in the main page TOC and thus discoverable.
So my “solution” that “fixes” it is: inject some JavaScript that hides all the files that aren’t
exported in the top level <code class="language-plaintext highlighter-rouge">index.ts</code>. It’s gross, but it’s good enough (Also: the
title of my autobiography).</p>
<p><strong>Disclaimer</strong>: This works for for my library (here’s the <a href="https://github.com/tensorflow/magenta-js/pull/409/files#diff-1853dafcee10b39c22a19539ff8fd11cR67">PR</a> I’m blessing our code
with this horror), but for your particular setup it might need some changes. I
speak very broken bash, so I probably can’t help you with those changes.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Variables I have:</span>
<span class="c"># Where your source is. We call the script from a different</span>
<span class="c"># place than the index.ts but maybe you don't.</span>
<span class="nv">srcDir</span><span class="o">=</span><span class="s2">"..."</span>
<span class="c"># Where you generate the docs. This could be a /docs folder, or a temp one</span>
<span class="c"># because you're going to push to the GitHub gh-pages branch.</span>
<span class="c"># I don't know what you do, I only know what we do (the latter).</span>
<span class="nv">docsDir</span><span class="o">=</span><span class="s2">"..."</span>
<span class="c"># The root index.ts file has a bunch of "export * from './foo';" lines.</span>
<span class="c"># Parse those lines into a space separated list of names. It's ok that</span>
<span class="c"># they're space separated, we'll split them in JS,</span>
<span class="c"># this is all a horror anyway. You might have to touch these regexes, sry.</span>
<span class="nv">exports</span><span class="o">=</span><span class="sb">`</span><span class="nb">sed</span> <span class="nt">-n</span> <span class="s2">"s/export </span><span class="se">\*</span><span class="s2"> from '.</span><span class="se">\/\(</span><span class="s2">.*</span><span class="se">\)</span><span class="s2">';/</span><span class="se">\1</span><span class="s2">/p"</span> <span class="nv">$srcDir</span>/src/index.ts<span class="sb">`</span>
<span class="c"># If your theme uses a different td class name than the one below,</span>
<span class="c"># inspecting it and update it in the selector. Also my names had</span>
<span class="c"># a bunch of extra quotes, hence the replacing, yours might not.</span>
<span class="c"># This is why I don't want to maintain this.</span>
<span class="nv">scriptToFixTheToc</span><span class="o">=</span><span class="s2">"<script> </span><span class="se">\</span><span class="s2">
const toc = </span><span class="se">\"</span><span class="nv">$exports</span><span class="se">\"</span><span class="s2">.split(' '); </span><span class="se">\</span><span class="s2">
const links = document.querySelectorAll('.tsd-kind-external-module'); </span><span class="se">\</span><span class="s2">
for (let i = 0; i < links.length; i++) { </span><span class="se">\</span><span class="s2">
const name = links[i].textContent.trim().replace(/</span><span class="se">\"</span><span class="s2">/g, ''); </span><span class="se">\</span><span class="s2">
if (toc.indexOf(name) === -1) { </span><span class="se">\</span><span class="s2">
links[i].parentNode.removeChild(links[i]); </span><span class="se">\</span><span class="s2">
} </span><span class="se">\</span><span class="s2">
} </span><span class="se">\</span><span class="s2">
</script>"</span>
<span class="c"># Inject that script in the index.html.</span>
<span class="nb">echo</span> <span class="nv">$scriptToFixTheToc</span> <span class="o">>></span> <span class="nv">$docsDir</span>/index.html
<span class="c"># Pray.</span>
</code></pre></div></div>
<p><br />
<br />
Like sands through the hourglass so are the hacks of our lives.</p>
monica.css2020-02-19T00:00:00+00:00https://meowni.ca/posts/monica-dot-css<p>Back in the day when I worked on Polymer I got used to relying on a bunch of useful CSS classes
that at the time we called <a href="https://github.com/PolymerElements/iron-flex-layout/blob/master/iron-flex-layout-classes.js">iron-flex-layout</a>.
They were there partly because flexbox was a sadness on IE and you needed to say everything 3 times to maybe
get it right twice, and add some
very special <code class="language-plaintext highlighter-rouge">flex-basis: 0.000000001px</code>
“bug fixes” that tbh nobody should ever have to write by hand. But they were also there because it’s kind of nice to say <code class="language-plaintext highlighter-rouge"><div class="horizontal"></code>
and for it to just work.</p>
<p>Some years later, it’s now 2020, and flexbox is really good everywhere! We don’t need <code class="language-plaintext highlighter-rouge">iron-flex-layout</code> anymore, but tbh I still
want to say <code class="language-plaintext highlighter-rouge"><div class="horizontal"></code> and for it to just work.</p>
<p>I know there are tons of CSS frameworks out there like <a href="https://tachyons.io/">tachyons</a> that can do this for me, but most of these frameworks
do <em>too much</em> for me. I don’t work on large projects that need design systems, and I don’t want every possible padding and margin and colour and flexbox configuration in the world. I just
want the ones that I know I end up using in every project. So here is <code class="language-plaintext highlighter-rouge">monica.css</code>: my very own CSS framework, which I copy paste at the beginning of every CSS file and take it from there. It’s already minified and bundled (because you copy pasted it) so dare I say: fast loading and efficient? 🙃</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">*</span> <span class="p">{</span><span class="nl">box-sizing</span><span class="p">:</span> <span class="n">border-box</span><span class="p">}</span>
<span class="o">[</span><span class="nt">hidden</span><span class="o">]</span> <span class="p">{</span><span class="nl">display</span><span class="p">:</span> <span class="nb">none</span> <span class="cp">!important</span><span class="p">}</span>
<span class="o">[</span><span class="nt">disabled</span><span class="o">]</span> <span class="p">{</span><span class="nl">pointer-events</span><span class="p">:</span><span class="nb">none</span><span class="p">;</span> <span class="nl">opacity</span><span class="p">:</span> <span class="m">0.3</span><span class="p">}</span>
<span class="nc">.horizontal</span> <span class="p">{</span><span class="nl">display</span><span class="p">:</span> <span class="n">flex</span><span class="p">;</span> <span class="nl">flex-direction</span><span class="p">:</span> <span class="n">row</span><span class="p">;</span> <span class="nl">justify-content</span><span class="p">:</span> <span class="n">space-between</span><span class="p">}</span>
<span class="nc">.vertical</span> <span class="p">{</span><span class="nl">display</span><span class="p">:</span> <span class="n">flex</span><span class="p">;</span> <span class="nl">flex-direction</span><span class="p">:</span> <span class="n">column</span><span class="p">}</span>
<span class="nc">.center</span> <span class="p">{</span><span class="nl">justify-content</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span> <span class="nl">align-items</span><span class="p">:</span> <span class="nb">center</span><span class="p">}</span>
<span class="nc">.flex</span> <span class="p">{</span><span class="nl">flex</span><span class="p">:</span> <span class="m">1</span><span class="p">}</span>
<span class="nt">html</span> <span class="p">{</span>
<span class="py">--spacing-xs</span><span class="p">:</span> <span class="m">8px</span><span class="p">;</span>
<span class="py">--spacing</span><span class="p">:</span> <span class="m">24px</span><span class="p">;</span>
<span class="py">--spacing-s</span><span class="p">:</span> <span class="m">12px</span><span class="p">;</span>
<span class="py">--spacing-m</span><span class="p">:</span> <span class="m">36px</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
Metronomes in JavaScript2019-09-10T00:00:00+00:00https://meowni.ca/posts/metronomes<style>
img.plot { max-height: 400px !important; }
</style>
<p>My job nowadays involves a lot of music and JavaScript. You know what musicians really
care about? Paychecks (support your local musicians, go to concerts, don’t steal music
from indie musicians). But also: keeping time.</p>
<p>Keeping time in JavaScript is kind of a joke, not just because time is a
social construct (this is the Jenn Schiffer social engineering at work), but because it’s really easy to write
code that blocks the timekeeper. Remember: JavaScript
inherently only has one thread, which it uses for everything: painting your buttons,
looping through arrays, mining bitcoin, scrolling. Everything.
This means that most of the time, you
write blocking code, but it only blocks for a little bit – 1ms here and there. And that’s ok!
Visually you don’t notice that kind of latency, and let’s be honest: it takes like 400ms to download the scripts, what’s 1ms?</p>
<p>1ms starts getting in the way when it’s actually 5ms, or 40 ms, or when you’re
trying to have a metronome run correctly. I made a <a href="https://input-delay.glitch.me">typing delay experiment</a> to see how much delay people could tolerate, and just
for typing alone some people got really antsy around 200ms (shout out
to the section of the population who thought they were heroes because they could
tolerate infinity delay because of how bad ssh latency is. That’s not heroic,
that’s Stockholm syndrome. Complain to your sys admins).</p>
<p>When I changed that to an <a href="https://audio-delay.glitch.me">audio delay experiment</a>,
musicians started complaining around 40ms. And that was just audio delay, not
an actual metronome. Imagine that fucking with your audio too! So, keeping time is really important – but how do we actually do that in JavaScript?</p>
<p>In general, when we want to not block in JavaScript (and do better than
<code class="language-plaintext highlighter-rouge">setInterval</code>, who is the friend you invite to a party but
shows up like +/- 4h to it), we do one of two things:
start writing <code class="language-plaintext highlighter-rouge">async</code> functions, or move code to a <code class="language-plaintext highlighter-rouge">Worker</code> (<a href="https://twitter.com/dassurma">Surma</a>
has a great <a href="https://dassur.ma/things/when-workers/">article</a> about workers everyone
should read). In
particular, for audio things, there’s a third option: using the Web Audio clock – Chris Wilson has a <a href="https://www.html5rocks.com/en/tutorials/audio/scheduling/">great blog post</a>
about how to do your own audio scheduling which is an oldie but a goodie! (turns out
not much changes in 4 years in the Web Audio spec world). Anyway, I wanted to
compare these three approaches, and see how bad the latency was.</p>
<h2 id="play-with-the-experiment">Play with the experiment</h2>
<p>Me being me, I <a href="https://metronomes.glitch.me/">made a whole demo</a> to
test and compare these approaches. I built
3 kinds of metronomes:</p>
<ul>
<li>a really bad one using <code class="language-plaintext highlighter-rouge">setInterval</code> on the main thread,</li>
<li>a less bad one using <code class="language-plaintext highlighter-rouge">setInterval</code> in a Worker,</li>
<li>the best one, that uses the Web Audio API to preschedule audio events, at
the times you want (labelled “prescheduled” in the graphs). The audio events
will happen precisely at the time they are scheduled, but if you want a
callback to do some visual work on, that callback needs to be in a <code class="language-plaintext highlighter-rouge">setTimeout</code>,
and will happen when it happens. This is why there are two lines for this metronome.</li>
</ul>
<p>You can run them on your own in that Glitch, but if you only want
the results, here they are.</p>
<h2 id="results">Results</h2>
<h3 id="setup">Setup</h3>
<p>There are 3 metronomes, that each tick 20 times, and after each tick, a callback
function is called. For the first 2 metronomes, in this callback you <em>also</em>
make the audio tick (except for the Web Audio scheduler metronome, which makes the audio
tick on its own time). The graphs below log the difference between the <code class="language-plaintext highlighter-rouge">audioContext.currentTime</code>
of successive ticks.</p>
<h3 id="-the-unrealistic-case">🤔 The unrealistic case</h3>
<p>This is when you’re literally doing 0 work in between the clock ticks. This is
probably never going to happen in a real app unless it’s … just
an actual metronome i guess. In this case, the difference between successive ticks looks
ok for all metronomes – I mean, why wouldn’t it be? You’re not scrolling, you’re
not doing any work, what’s there to block the ticks? There’s still a bit of variance between
each ticks, but that’s because we know we can’t schedule anything (except for the Web Audio
clock) to be <em>exactly</em> 0.5s away.</p>
<p><img class="plot" alt="" src="/images/metronomes/1.png" /></p>
<h3 id="-the-awful-case">🤢 The awful case</h3>
<p>Here we are doing 0.5 seconds of fake work on the main thread, after each tick. This
is where things get really dodgy. Because that fake work is blocking, that means that <em>all</em>
the metronome callbacks are kind of screwed, and their ticks are delayed by at least 0.5s.
In the second metronome, even though we’re calling <code class="language-plaintext highlighter-rouge">setInterval()</code> in a Worker, it makes no difference because the work from the previous tick is blocking, so it automatically delays the next tick.
In the Web Audio case, we can hear the ticks correctly (the green line), but the callback (which you would use to display things to the screen), is delayed for the same reason
as the other metronomes. Friends don’t let friends do work on the main thread.</p>
<p><img class="plot" alt="" src="/images/metronomes/2.png" /></p>
<h3 id="-the-better-but-still-not-great-case">😰 The better, but still not great case</h3>
<p>When we have a big chunk of blocking work, a good approach is to chunk it up in
smaller work. There are several ways to do this. I split each 0.5s of work into smaller
5ms chunks, and then do each of them in a <code class="language-plaintext highlighter-rouge">requestAnimationFrame</code>. This is ok,
but a bit wasteful (it makes your work take longer than necessary). A better
approach is to use tasks (see this <a href="https://github.com/GoogleChromeLabs/proxx/blob/b1fa3b4c7a8565ddc245b03680dadd567c3a8f9e/src/utils/scheduling.ts#L20-L34">sample code</a> from the proxx game),
but the results weren’t going to be that different in this case, so I didn’t bother.
Anyway, this experiment looks better!
Now our ticks are only delayed by about 5ms, which might be ok for your use case. The bad main
thread <code class="language-plaintext highlighter-rouge">setInterval</code> metronome is still doing poorly because there’s still
work on the main thread and it keeps time on the main thread, so time is still
wibbly wobbly in this case.
<img class="plot" alt="" src="/images/metronomes/3.png" /></p>
<h3 id="-the-optimal-case">🤩 The optimal case</h3>
<p>All workers all the time! If you can, do all this expensive work in a Worker!
If we move the work we have to do in the callback completely off the main thread,
then this setup basically looks the same as the unrealistic “there’s no work being done ever”
case – the key distinction is that it’s really “there’s no work being done <em>on the main thread</em> ever. Hurray!</p>
<p><img class="plot" alt="" src="/images/metronomes/4.png" /></p>
<h2 id="what-have-i-learned-from-this">What have I learned from this</h2>
<ul>
<li>time is hard</li>
<li>I knew <code class="language-plaintext highlighter-rouge">setInterval()</code> is bad for time keeping, but now I know it’s like … really bad</li>
<li>if you need audio scheduling, use the Web Audio clock</li>
<li>if you need accurate scheduling without the Web Audio clock, use <code class="language-plaintext highlighter-rouge">setInterval</code>
in a Worker</li>
<li>and if you can, move any expensive work that you have to do from the main thread
to a Worker.</li>
</ul>
<p>Hope this helps at least one of you!</p>
<hr />
<p>Thanks to <a href="https://twitter.com/dassurma">Surma</a> for proof reading this and letting
me steal his horrific “block for a fixed time” sample code (it’s <a href="https://glitch.com/edit/#!/metronomes?path=script.js:151:1">this</a>. I know you want to look).</p>
The perils of tensor.dataSync()2019-02-22T00:00:00+00:00https://meowni.ca/posts/on-tfjs-datasync<p>One of the first things you stumble on when you start using TensorFlow.js is
that sometimes you need your data as a Tensor, and sometimes you need it
as a JavaScript number. Maybe it’s for logging it, maybe it’s for displaying
it somewhere during training, maybe it’s because you don’t trust the robots
to be better than you at math.</p>
<p>This is a quick post that tries to clarify why doing this <strong>synchronously</strong>
is probably bad and will leave your UI really janky. <a href="https://twitter.com/nsthorat">Nikhil</a>
(who like, birthed TensorFlow.js, bless) was kind enough to explain this to me recently, so I figured
I’d return the favour, with fewer meeps and more mistakes.</p>
<h2 id="downloading-and-uploading">Downloading and Uploading</h2>
<p>When you create a Tensor, it lives on the CPU. The mere fact that it’s a Tensor
doesn’t automatically move that data into its GPU mansion – it needs to be used in a WebGL
program. (I’m playing fast and loose here with the words GPU and CPU btw, so
hold back the pedantics: when I say “it lives on the CPU” I mean “in main memory,
where the CPU processes stuff”; the GPU has it‘s own memory, that’s where it
processes stuff, and that’s where that data has to get transferred to.
It’s fine. You know I know.)</p>
<p>You <strong>upload</strong> the tensor to the GPU when you call one of the <code class="language-plaintext highlighter-rouge">tf.</code> operations on it.
Tensor operations are matrix math, and matrix math is really fast on the GPU,
so every time you call something like <code class="language-plaintext highlighter-rouge">sum</code> or <code class="language-plaintext highlighter-rouge">sqrt</code> on a Tensor, TensorFlow.js
creates a little <a href="https://js.tensorflow.org/tutorials/custom-webgl-op.html">WebGL operation</a>,
and sends it to the backend. Whatever data lived on the CPU is now
“uploaded” to the GPU (to a WebGL texture).</p>
<p>You <strong>download</strong> a Tensor when you want to get that data from the GPU back onto
the CPU. The data now lives in a WebGL texture, so TensorFlow.js needs to call
<a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/readPixels"><code class="language-plaintext highlighter-rouge">readPixels</code></a>
to … read… those pixels… from the texture and convert them back into something you can use.
Here’s the problem: calling <code class="language-plaintext highlighter-rouge">readPixels</code> is fundamentally a blocking operation: when you
ask the GPU to give you data, you <em>have</em> to wait for it to respond; this means
you can’t really do anything else on the screen while this is happening, like
paint any animations.</p>
<p>TL;DR:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>const a = tf.tensor(); // a is on the CPU.
const b = a.sqrt(); // Upload a's data to the GPU.
const c = a.dataSync(); // Download a's data from the GPU to the CPU.
</code></pre></div></div>
<p>So the problems here are:</p>
<ul>
<li>calling <code class="language-plaintext highlighter-rouge">readPixels</code> will make your UI janky.</li>
<li>downloading and uploading from the GPU isn’t free, so doing this over and over
is bad news bears.</li>
<li>downloading from the GPU synchronously over and over is a 2-in-1 and
will probably murder your favourite pet.</li>
</ul>
<h2 id="how-it-works">How it works</h2>
<p>If you read the latest <a href="https://js.tensorflow.org/api/0.15.1/"><code class="language-plaintext highlighter-rouge">0.15.1</code></a> docs,
you’ll discover that there are at least 4 ways of “downloading” your tensor:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">aTensor.array()</code> – asynchronous, and keeps the shape of the tensor (so it returns a nested array)</li>
<li><code class="language-plaintext highlighter-rouge">aTensor.arraySync()</code> – same as above but <strong>synchronous</strong></li>
<li><code class="language-plaintext highlighter-rouge">aTensor.data()</code> – asynchronous and doesn’t keep the shape of the tensor (and returns a fancy <code class="language-plaintext highlighter-rouge">Float32Array</code> like type)</li>
<li><code class="language-plaintext highlighter-rouge">aTensor.dataSync()</code> – same as above but <strong>synchronous</strong></li>
</ul>
<p>Out of these, I personally prefer the new <code class="language-plaintext highlighter-rouge">array</code> flavours, since I think about my
tensors based on their dimensions, so when they get flattened I get confused.</p>
<p>The difference between the sync and async versions is that:</p>
<ul>
<li>for the sync methods, TensorFlow.js just goes ahead and calls <code class="language-plaintext highlighter-rouge">readPixels</code>,
which instantly blocks and causes sadness.</li>
<li>for the async methods, it creates a <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/fenceSync">“fence”</a> (think of it like a fancy WebGL <code class="language-plaintext highlighter-rouge">setTimeout</code>),
and then calls a different method, <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/getBufferSubData"><code class="language-plaintext highlighter-rouge">getBufferSubData</code></a>
when that fence is hit. Unlike <code class="language-plaintext highlighter-rouge">readPixels</code>, this doesn’t block the UI thread
and it won’t cause sadness.</li>
</ul>
<p>If you, like me, have strange hobbies and want to find this in the actual
TensorFlow.js source code, check out the <code class="language-plaintext highlighter-rouge">read</code> and <code class="language-plaintext highlighter-rouge">readSync</code> methods in
<a href="https://github.com/tensorflow/tfjs-core/blob/master/src/kernels/backend_webgl.ts">this file</a>.</p>
<h2 id="what-to-do">What to do</h2>
<p>My advice is:</p>
<ul>
<li>if you have to download your data, try to do it once, asynchronously. Do this
at the end, after all your GPU computations are done.</li>
<li>reach towards the <strong>async</strong> versions first – that way, even though the
operation is expensive, it won’t block the UI and you can do other non-janky
things like letting the user scroll on the page.</li>
<li>if you really really pinky swear have to use the sync version, just take
another look at the code and see if you can’t move that call somewhere else
where it can by async.</li>
</ul>
I did an inktober and I want to tell you about it2018-11-15T00:00:00+00:00https://meowni.ca/posts/inktober<p>Inktober is a project where artists make an ink drawing every day for the
whole month of October. This year I did an inktober but ignored all the rules, and
made Internet Stuff™️ instead. That experiment lives <a href="https://howthee.glitch.me">here</a>, but
I want to tell you why I did it before you go ahead and judge it. I
think that it’s also important to tell you it was a huge pain in the ass just
in case you watched it unfold and thought I magically stuck every landing.</p>
<h2 id="the-why">The why</h2>
<p>“Art” is a word I struggle with a lot. I don’t think of myself as an artist, because
none of the things I make feel like art yet. At the same time, I obsess about these
things if I don’t feel they represent <em>me</em>, which is one of the
nigthmares of being an artist – hence the struggle. For example, I made pottery for well over a year
before I thought any of the mugs looked like what I wanted them to look like.
They looked fine, and they looked like mugs, but they weren’t the kind of mugs
I <em>wanted</em> to make.</p>
<p>In my opinion, one of the qualities you need to be an artist is to be
extremely creative. I am not extremely creative, but I believe that everything
in life can be learnt by grinding that level. So with inktober I wanted to
force myself to think creatively every day, for a whole month, and see if that
would level up my creativity.</p>
<h2 id="the-how">The how</h2>
<p>I don’t like open ended projects because I think they lead to
intellectual wankery instead of like, <em>actually doing the thing</em>, so I made up these rules
for my weird art experiment:</p>
<ul>
<li>it had to be somehow about the sheriff of circle punch [1]</li>
<li>it shouldn’t take more than like, an hour</li>
<li>it’s fine if I skipped weekends [2]</li>
</ul>
<p>[1] Honestly the first day of inktober was a joke to make fun of the hashtag
and my friend <a href="https://twitter.com/fabrahamlincoln">Fabian</a>, who I troll
with a <a href="https://www.urbandictionary.com/define.php?term=Circle%20Game">circle punch</a>
any time I can. This of course ended up trolling me, because I did the whole fucking month
afterwards, so ain’t that the troller becoming the trollee. My friend <a href="https://twitter.com/goatsandbacon">Bushra</a> came up with the name (“it’s always howdy,
never howthee”). <a href="https://twitter.com/search?q=from%3A%40goatsandbacon%20%23inktober&src=typd">Her inktober</a> was way better.</p>
<p>[2] I don’t work weekends, and if anyone tells you that “being creative”,
or “making art” doesn’t feel like work, don’t believe them. Nothing is a free lunch.</p>
<h2 id="the-aftermath">The aftermath</h2>
<p>I learnt a lot of CSS. A lot of the days ended up with me looking at Codepen
and trying to figure out what animation I could use that day. I learnt about
svg filters, and became really comfortable writing keyframes. I finally
managed to write <code class="language-plaintext highlighter-rouge">align-items: center; justify-content: space-between;</code>
without having to look it up. I only crashed my browser once.</p>
<p>Overall it was really, really hard, and I hated a lot of it.
A bunch of the days <a href="https://howthee.glitch.me/day-5.html">were</a> <a href="https://howthee.glitch.me/day-6.html">political</a>. Some of the days were <a href="https://howthee.glitch.me/day-8.html">freebies</a>. <a href="https://howthee.glitch.me/day-18.html">Day 18</a>
was my favourite. <a href="https://howthee.glitch.me/day-10.html">Day 10</a> almost
made me throw my laptop at the cat.</p>
<p>And you know what? I think it worked. I noticed that I was walking around all day
<em>looking</em> at everything, like artists look at everything, trying to see
what I could use for that day’s inktober. Once, when I was in
highschool, an art teacher set me up on a mentoring date with an art school major.
I told her half of the time I didn’t know what to paint, and she told me to
just look at things and make them weird. “It’s like the Eiffel tower, but like
melting over a bridge, you know?”. I didn’t know, but then on day 18
I took a drum beat and made it into a Franken-morphed sheriff. I wrote down
every idea that I had, and after the month was over, I still had ideas left over.
They’re not all good, but you can afford being picky when you’re out of the drought.</p>
<hr />
<div class="glitch-embed-wrap" style="height: 900px; width: 100%;">
<iframe allow="geolocation; microphone; camera; midi; encrypted-media" src="https://glitch.com/embed/#!/embed/howthee?path=README.md&previewSize=100" alt="howthee on Glitch" style="height: 100%; width: 100%; border: 0;">
</iframe>
</div>
Hello magenta2018-11-07T00:00:00+00:00https://meowni.ca/posts/hello-magenta<p><a href="https://magenta.tensorflow.org">Magenta.js</a> is a JavaScript library that helps you generate art and music
on the web. It’s also the team that I work on now! One of the things that I do
whenever I join a new team is learn a bunch of things, and then make a bunch of
tutorials that past Monica would’ve loved to stumble over. This is one of them!</p>
<p>In this tutorial, we’ll talk about the music generation bits in
<code class="language-plaintext highlighter-rouge">@magenta/music</code> (one of the several libraries in Magenta.js) –
how to make your browser sing, and in particular, how to make your browser
sing <em>like you</em>!. The tutorial is interactive, and introductory
and <a href="https://hello-magenta.glitch.me/">on Glitch</a>. Go play with
all the examples, and have fun! 💕</p>
Hello tensorflow2018-05-22T00:00:00+00:00https://meowni.ca/posts/hello-tensorflow<style>
iframe {
height: 640px;
width: 100%;
margin: 0 auto;
border: 5px solid #E0F7FA;
border-radius: 3px;
}
</style>
<p><strong>Machine Learning (ML)</strong> is the dope new thing that everyone’s talking about, because it’s really good
at learning from data so that it can predict similar things in the future. Doing ML by hand is pretty annoying
since it usually involves matrix math which is zero fun in JavaScript (or if you ask me: anywhere 😅).
Thankfully, <a href="https://js.tensorflow.org/">TensorFlow.js</a> is here to help! It’s
an open source library that has a lot of built-in Machine Learning-y things like models and algorithms so that
you don’t have to write them from scratch.</p>
<h2 id="is-your-problem-a-machine-learning-problem">Is your problem a Machine Learning problem?</h2>
<p>Machine learning is good at classifying and labelling data. The premise of every machine learning problem is:</p>
<ul>
<li>Someone gives us some data that was generated according to a <strong>secret</strong> formula. This data could be a bunch of points (that are generated based on some math equation), but could also be fun, like images (the secret formula could be “some of these images are chihuahuas and some are
<a href="https://mashable.com/2016/03/10/dog-or-muffin-meme/#LjBd4.e9lgqJ">blueberry muffins</a>) or bus schedules.</li>
<li>By looking at this data we were given, we approximate the secret formula so that we can correctly predict a future data point. For example, if we’re given a photo, we will eventually be able to confidently say whether it’s a dog or a muffin.</li>
</ul>
<h2 id="a-fun-demo">A fun demo!</h2>
<p>If you want to get started, predicting numbers tends to be easier than
predicting images, so in this example we’re trying to fit a curve to a bunch of
data (this is the same example from the
<a href="https://js.tensorflow.org/tutorials/fit-curve.html">TensorFlow</a>
site but with waaaaay more code comments and a prettier graph).</p>
<p>We are given a bunch of points (for <code class="language-plaintext highlighter-rouge">x</code> between -1 and 1, calculate a <code class="language-plaintext highlighter-rouge">y</code> according to
<code class="language-plaintext highlighter-rouge">y = a * x^3 + b * x^2 + c * x + d</code> – we know this is the secret formula but we don’t know the
values of those <code class="language-plaintext highlighter-rouge">a,b,c,d</code> coefficients.)
Our goal is to learn these coefficients, so that if we’re given a new <code class="language-plaintext highlighter-rouge">x</code> value, we can say what the <code class="language-plaintext highlighter-rouge">y</code> value should be.</p>
<p>The <strong>blue</strong> dots are the training points we were given. The <strong>red</strong> dots would be our guesses,
based on our initial, default coefficients (hella incorrect!). Once you click the <code class="language-plaintext highlighter-rouge">train</code>
button, the <strong>green</strong> dots show how our coefficients are getting better. After you see the default
example, check what happens if you change the shape of the data, or we are given fewer data points or fewer iterations!</p>
<iframe src="https://hello-tensorflow.glitch.me/index.html#demo-content" frameborder="0" scrolling="no"></iframe>
<h2 id="how-it-works">How it works</h2>
<p>Most machine learning algorithms follow this pattern:</p>
<ul>
<li>We have to figure out the <strong>“features”</strong> of the secret formula that generated the data we were given, so that we
can learn them. In my opinion, this is like 80% of the complexity of solving an ML problem. In this example, we were told the shape of the secret formula (it’s a cubic!), so the features we have to learn are the coefficients in the polynomial. For something more
complex like the “is this a dog or a blueberry muffin” problem, we’d have to look at pixels and colours and formations and what
makes a dog a dog and not a muffin.</li>
<li>Once we figure out these features (in our case, those <code class="language-plaintext highlighter-rouge">a,b,c,d</code> coefficients), we initialize them to some random values. We could now use them to make
predictions, but they would be teeeeeerrible because they’re just random.</li>
<li>(I’m just going to use our actual example from now on and
not dogs)</li>
<li>We start looking at every piece <code class="language-plaintext highlighter-rouge">(x,y)</code> of training data we were given. We take the <code class="language-plaintext highlighter-rouge">x</code> value, and based on these coefficients we have estimated, we predict what the <code class="language-plaintext highlighter-rouge">y</code> value would be.
We then look at the correct <code class="language-plaintext highlighter-rouge">y</code> value from the original training data, calculate the
difference between the two, and then adjust our coefficients so that our predicted value gets closer to the correct one.</li>
<li>(this, with more math sprinkled in is called “stochastic gradient descent”. “Stochastic” means probabilistic, and
“gradient descent” should make you think of walking down a hill, towards a sink hole – the higher the hill, the bigger the prediction error, which is why you want to descend towards the error-free hole.)</li>
<li>This part of code is actually pretty messy (because matrices and derivatives), and TensorFlow does this for us!</li>
<li>We keep doing this until we use up all the data, and then repeat the entire process so that we iterate over the same data over
and over again until at the end we’ve pretty much learnt the coefficients!</li>
</ul>
<h2 id="the-code">The code</h2>
<p>You can look at the code for the demo <a href="https://glitch.com/edit/#!/hello-tensorflow?path=script.js:95:10">on Glitch</a>. I tried to comment
most lines of the code with either what the algorithm or TensorFlow are doing (especially when
TensorFlow is actually doing a looooot of heavy lifting behind the scenes). I hope it helps!</p>
How browsers position floats2018-04-11T00:00:00+00:00https://meowni.ca/posts/float-layout<style>
iframe {
height: 800px;
width: 100%;
margin: 0 auto;
border: 5px solid #E0F7FA;
border-radius: 3px;
}
</style>
<p>When you have a <code class="language-plaintext highlighter-rouge">float</code> CSS property on a box (with a value different than <code class="language-plaintext highlighter-rouge">none</code>), this box
must be laid out according to the <strong>float positioning algorithm</strong>. Loosely, it says:</p>
<ul>
<li>if the box has <code class="language-plaintext highlighter-rouge">float:left</code>, the box is positioned at the beginning of the line box</li>
<li>if the box has <code class="language-plaintext highlighter-rouge">float:right</code>, the box is positioned at the end of the line box</li>
<li>text (and more generally anything within the normal, non-floaty flow) is laid out along the edges of the floating boxes</li>
<li>the <code class="language-plaintext highlighter-rouge">clear</code> property changes the floating behaviour.</li>
</ul>
<p>Anyway, in general you’ll have a better time if you use a flexbox or CSS grid instead of floats, because floats are quirky and have strange edge cases, but if you were ever curious
about how the algorithm would choose where to position different floats, here’s a demo
(which you can also play with directly on <a href="https://float-layout.glitch.me">glitch</a>):</p>
<iframe src="https://float-layout.glitch.me/demo.html" frameborder="0"></iframe>
An intro to Reinforcement Learning (with otters)2018-02-26T00:00:00+00:00https://meowni.ca/posts/rl-with-otters<style>
img.otter { max-height: 220px !important; }
iframe.otter {
height: 800px;
width: 100%;
margin: 0 auto;
border: 5px solid #E0F7FA;
border-radius: 3px;
}
</style>
<p>Before I wrote the JavaScripts, I got a master’s in AI (almost a decade ago 🙀),
and wrote a <a href="/includes/mdinculescu_thesis.pdf">thesis</a>
on a weird and new area in Reinforcement Learning. Or at least it was new then.
It’s definitely still weird now. Anyway, I loved it. With all the hype around Machine Learning
and Deep Learning, I thought it would be neat if I wrote a little primer
on what Reinforcement Learning really means, and why it’s different than
just another neural net.</p>
<p>Richard Sutton and Andrew Barto wrote an <em>amazing</em> book called
“Reinforcement Learning: an introduction”; it’s
my favourite non-fiction book I have ever read in my life, and it’s why I fell
in love with RL. The complete draft is available
for free <a href="http://incompleteideas.net/book/bookdraft2017nov5.pdf">here</a>, and if you’re into math,
and want to explore this topic further, I can’t recommend it enough.</p>
<p>If you’re not into math, I have otters.</p>
<p><img class="otter" alt="otter says hi" src="/images/2018-02-26/0.png" /></p>
<h2 id="what-is-it">What is it?</h2>
<p>Reinforcement learning (or RL) solves a very specific problem: figuring out
how to act over time, so that you get the most long term reward.
Both these sequences of actions and the reward bit are important components
that make RL a “good” approach to solve a problem.</p>
<p>For example, this is perfect if you’re a Roomba who is trying to get home
(the only reward you get is if you
actually get home, so while you’re roaming around aimlessly and get no 💰,
you have a feeeeeeeling you’re not doing it right).</p>
<p>On the other hand, this is terrible if you’re
trying to figure out if a photo has an otter in it; there are no
sequences of actions that matter here, other than doing the decision of saying
“yes iz otter”. You’re just trapped in a room where people slip Polaroids of
animals under the door and you have to tell them what it is. Nightmares aren’t
really a good area for RL.</p>
<p><img class="otter" alt="i'm doing RL" src="/images/2018-02-26/1.png" /></p>
<h2 id="what-isnt-it">What isn’t it?</h2>
<p>There are many things with the word “learning” in them that <em>aren’t</em> Reinforcement Learning.</p>
<ul>
<li><em>supervised learning</em>. This is a kind of Machine Learning where someone
gave you a training set that has everything labelled correctly, you learn from
it, and hope that at exam time what you’ve learnt is correct. This is the “I have
1000 images of cats, now I can tell you if this new image is a cat” problem.</li>
<li><em>unsupervised learning</em>. This is another kind of Machine Learning where
someone gave you a bunch of data with no labels, and just by staring at it you
try to find structure in it and make up labels. This is the “I have 1000 images of
cats and dogs, but nobody told me what a cat or a dog looks like; now I can tell you
if this new image is like what I call a cat or a dog”.</li>
</ul>
<p><em>Classification</em> is a very common problem that can be solved with both of these
Machine Learning approaches (but can’t be solved very well with RL, which isn’t
really suited for one-off actions).</p>
<p><a href="https://en.wikipedia.org/wiki/Artificial_neural_network">Neural nets</a> are very good at solving these 2 kinds of Machine Learning problems. For example, the secrets straight out of your <a href="https://secure.i.telegraph.co.uk/multimedia/archive/03370/doge_3370416k.jpg">nightmares</a>
are created by a “deep” neural net, a neural net that has several layers in between
its input and output layers.</p>
<p>If you add neural nets on top of some Reinforcement Learning algorithms, you get something
called <em>Deep Reinforcement Learning</em>, which is a brand new area of research that
brought you supercomputers that <a href="https://en.wikipedia.org/wiki/AlphaGo">win at Go</a>.</p>
<h2 id="the-world">The world</h2>
<p>RL problems are usually set up in an environment that is built out of <strong>states</strong>,
and you can move between them by taking <strong>actions</strong>. Once you take an action,
you’re given a <strong>reward</strong>, and you keep doing this until someone
tells you to stop.</p>
<p>In the Roomba example, the states could be the <code class="language-plaintext highlighter-rouge">(x,y)</code>
positions on the map, and you move between two states (i.e. locations) by moving the
motors in a particular direction. The reward might be set up in such a way that
you only get <code class="language-plaintext highlighter-rouge">1</code> point if you reach the home base, and <code class="language-plaintext highlighter-rouge">0</code> otherwise. If there’s
particularly dangerous spots in the world you want to make sure the Roomba
learns to avoid (like cliffs or a cat), you can make sure any actions that end
up in those states get a reward of <code class="language-plaintext highlighter-rouge">-1</code>.</p>
<p>Some environments are less like real worlds and more
like abstract worlds: when you’re playing Texas Hold’em poker, the state that you’re in
could be the hand that you have, and what cards are on the table, and the actions
could be <code class="language-plaintext highlighter-rouge">folding</code>, <code class="language-plaintext highlighter-rouge">raising</code>, <code class="language-plaintext highlighter-rouge">checking</code>. If you only give a reward at the end of the game (eg.
“I won this hand or I didn’t”), it’s very hard to know how you’re actually
doing. These problems have much more complicated reward signals (and tbh, states): how players
are doing, which are staying, how they’re playing, need to be considered.</p>
<p><img class="otter" alt="this is otterly rewarding" src="/images/2018-02-26/2.png" /></p>
<p><strong>Nerd alert</strong>: If you’re interested in the math behind this, the environments are usually
represented by a <a href="https://en.wikipedia.org/wiki/Markov_decision_process">Markov Decision Process</a> (MDP), or a <a href="https://en.wikipedia.org/wiki/Partially_observable_Markov_decision_process">Partially Observable
Markov Decision Process</a> (POMDP). The difference between the two is that in the latter case you’re not told
exactly what your state in the world is (you’re a GPS-less Roomba). You still
know what actions you took, and what reward you’re accumulating, but since you
don’t know what they <em>actually</em> mean in the world, you have to make up your own
representation of it. These are typically harder and weirder problems, and these
were the ones I was focusing my research on, btw!</p>
<h2 id="learning-how-to-act">Learning how to act</h2>
<p>Ok, so: we’re a Roomba, we’ve been placed somewhere in a world, and we have a
goal: to get home (I think this technically makes us ET, but hey). The thing that
tells us which action to take in a state is our <strong>policy</strong>. If we can figure out
the best action to take in every state in the world, then we have an <strong>optimal
policy</strong>.</p>
<p><img class="otter" alt="clear eyes, optimal policy, can't lose" src="/images/2018-02-26/3.png" /></p>
<p>In order to figure out if a policy is better than another, we need to figure out how “good”
it is to be in a certain state according to that policy (because then you get to compare them:
from this state, one policy leads me to a pot of gold, and one to
sudden death. One is clearly superior). We call this the <strong>value of a state</strong>,
and it’s basically the reward we <em>expect we’re going to get</em> from that state if
we follow what the policy tells us to do.</p>
<p>The <strong>expected reward</strong> bit is subtle
but hella important: if you just care about immediate reward, a state that doesn’t lead you
to instant death sounds pretty good! However, if you keep taking these seemingly-ok-because-they-didn’t-kill-us actions,
you might still end up at the edge of the cliff, one step away from instant death. By
considering reward a number of steps away, you don’t get trapped in shitty trajectories like this.</p>
<p>Most basic RL algorithms try to learn one of these functions:</p>
<ul>
<li>the <strong>state-value function</strong>, which is the value of every state in the world. This
is usually called <code class="language-plaintext highlighter-rouge">V</code> (for value)</li>
<li>the <strong>action-value function</strong>, which is the value of taking an action from a state,
for all actions and states in the world. This is usually called <code class="language-plaintext highlighter-rouge">Q</code> (for qaction? lolmath.)</li>
</ul>
<p>The difference between the two is potentially religious. The <strong>state-value</strong>
function basically says “where you are in the world is important, so figure out
the sequence of good states and follow that”. The <strong>action-value</strong> function says “we’re in a state, and
some of the actions we can take are awesome, and some are terribad, figure out the awesome ones”.</p>
<p>The point of an RL algorithm is to basically learn these functions, and then
pick the one with the highest value: that’s your optimal policy!</p>
<h2 id="how-do-we-learn">How do we learn?</h2>
<p>We learn things about the world by exploring the world. You can think about it
as roaming the world in “practice mode”, which gives you experience, which helps
you learn what your policy is (what to do in a particular state).
When it’s “exam time mode”, you use the policy you’ve learnt and act according
to that. The more data you have, the better you learn.</p>
<p>If we think about our <strong>practice policy</strong> as the way we decided to act
while in practice mode, and our <strong>optimal policy</strong> as the way we will act during
“exam time” (always be the very best you at exams), then there are
two fundamentally different ways in which you can learn:</p>
<ul>
<li><strong>on-policy learning</strong>: in practice mode, you are following the <strong>practice policy</strong> to
explore the environment, and learning
how well it works. the more you learn, the better it gets. in “exam time mode”, you still use this <strong>practice policy</strong> you’ve perfected.</li>
<li><strong>off-policy learning</strong>: in practice mode, you are following the <strong>practice policy</strong> to
explore the environment, and learning what the <strong>optimal</strong> policy should look like,
based on what you’re discovering. in “exam time mode”, you would use the <strong>optimal policy</strong>
you’ve been learning.</li>
</ul>
<p><img class="otter" alt="i'm an on policy otter, my policy is to always say yes to food" src="/images/2018-02-26/4.png" /></p>
<h2 id="and-now-a-code">And now, a code!</h2>
<p>My favourite favourite FAVOURITE thing about AI is that if you
do a simple thing, you can get a very satisfying demo. There are tons of Reinforcement
Learning algorithms out there: some are very complicated and take a lot of math. But
some are very simple, and that’s the one I <a href="https://glitch.com/edit/#!/q-learning">implemented</a> for you.</p>
<p>It’s called <strong>Q-Learning</strong>, because it learns the <code class="language-plaintext highlighter-rouge">Q</code> function (if you forgot:
this is the action-value function, i.e. the value of all of the actions,
from all of the states). It works like this:</p>
<ol>
<li>Initialize your <code class="language-plaintext highlighter-rouge">Q</code> function randomly (so the value of any action from
any state is a random number). This bit is important so that you don’t accidentally
bias your policy with lies</li>
<li>Start in a random state (call it <code class="language-plaintext highlighter-rouge">S</code>).</li>
<li>From this state, we need to figure out
how to move in the world. We’re gonna
do something slightly fancy called <code class="language-plaintext highlighter-rouge">epsilon-greedy</code>: most of the time, we’re going to move
according to what the policy says (“greedily”). However, <code class="language-plaintext highlighter-rouge">epsilon</code> percent of the time, we’re going to move randomly. This means that we still get to do some random exploration, which
is important to make sure we see new states we might not otherwise.
<code class="language-plaintext highlighter-rouge">epsilon-greedy</code> is loooooved by RL people because it balances “exploration” (doing
things randomly) with “exploitation” (doing things correctly) and you’ll
find it in like literally every RL paper out there.</li>
<li>And…take that action! Once you take it, you’ll end up in a state <code class="language-plaintext highlighter-rouge">S_2</code>,
and the world tells you what reward you
got. Call it <code class="language-plaintext highlighter-rouge">R</code>. We’re going to use this reward to update our <code class="language-plaintext highlighter-rouge">Q</code> function for
the state we were in, and the action we took; more precisely: we’re going to update our <code class="language-plaintext highlighter-rouge">Q(S,A)</code>
value. Note how you basically always update the <em>previous</em> state-action pair, by seeing the
results of that action in the world.</li>
<li>The update step is a bit mathy, so I’ll spare you it (here’s the <a href="https://glitch.com/edit/#!/q-learning?path=q-learner.js:73:32">relevant code</a> if you want
to check it out), but the TL;DR is: if this action was a good action,
then the state that we ended up in should be a better state than the one we
were currently in (closer to the goal). If we got a bad reward, then we reduce
the value of <code class="language-plaintext highlighter-rouge">Q(S,A)</code>; if we didn’t, then we increase it.</li>
<li>boring note incoming: this is an <code class="language-plaintext highlighter-rouge">off-policy</code> algorithm. How we calculate the
<code class="language-plaintext highlighter-rouge">Q(S,A)</code> values isn’t affected by how we actually moved in the world; we assume
we followed the <code class="language-plaintext highlighter-rouge">greedy</code> (aka best) policy, even if we didn’t.</li>
<li>Anyway, now, we’re in a new state, so back at Step 2. Repeat Steps 2-6 until you
end up in a goal state. Once you do (yay!), you can go back to Step 1 and start
in a new random state (this is important so that you see new parts of the world!).</li>
</ol>
<p>If you do this enough times, you eventually experience enough of the world that
your <code class="language-plaintext highlighter-rouge">Q</code> function will tell you what to do!</p>
<p><img class="otter" alt="get otter here, we otter see a demo!" src="/images/2018-02-26/5.png" /></p>
<h2 id="demo">Demo</h2>
<p>This is a gridworld! It has a goal state, and a blob can move in any direction from
any state. If you press play before doing any learning, the blob will just
walk around randomly. If you press the learn button, the blob will take <a href="https://glitch.com/edit/#!/q-learning?path=index.html:64:6">10000 steps</a>
around the world and learn the optimal policy. I also plotted a heatmap of the Q function (the
greener the square, the higher its value is). States close to the goal are more important,
and this makes sense!</p>
<p>You can check out that glitch, clone it, and play with that
value. If you take far fewer steps (like 5000), you’ll see that your policy isn’t perfect
everywhere around the world (you might see the blob get stuck in circles a lot, far away from the goal,
because it hasn’t explored that area well enough yet).</p>
<iframe class="otter" src="https://q-learning.glitch.me/" frameborder="0"></iframe>
<hr />
<p>Hope this was useful! I wanted to write this post because I read this awesome
<a href="https://www.alexirpan.com/2018/02/14/rl-hard.html">article</a> by Alex Irpan about
the problems with Deep Learning, but I didn’t know who to share it with, because
I don’t really hang out with RL researchers anymore. So instead, I decided to
teach you (YES, YOU!) some Reinforcement Learning, so that you can now read
that article and not be lost in it. Yay? Yay!</p>
<p>Thanks to <a href="https://twitter.com/danlizotte">Dan Lizotte</a> for reading this, even
though he really didn’t have to.</p>
Automatic visual diffing with Puppeteer2018-01-31T00:00:00+00:00https://meowni.ca/posts/2017-puppeteer-tests<p>So testing, right? We should do it. The thing is, testing is hard, and good testing
is <em>reaaaaaaally</em> hard, and tbh I’m pretty bad at testing. So I end up not
testing my apps, and then I feel guilty about it, but I’ll stop you now:
you can’t run guilt on Travis. If this sounds familiar, then this blog post is for you.</p>
<p>I did a little song-and-dance that sets up <a href="https://github.com/GoogleChrome/puppeteer">Puppeteer</a>*
, takes screenshots of your app (like, all the routes you care about), and
then compares them to the “golden” ones. If they match, your test passes!
Yes, it only works on Chrome. No, it’s not
actually unit testing. Yes, it’s literally just counting pixels but you know what?
It counts them in both a wide and a narrow viewport size and any testing is better
than no testing at all; fight me.</p>
<p>* Puppeteer is an <code class="language-plaintext highlighter-rouge">npm</code> library that lets you control Chrome. You know, like a puppet.
In particular, Puppeteer makes
it super easy to take screenshots (and click on things in your page). It’s
like a waaaaaaay less infuriating Selenium, but infinitely harder to spell.</p>
<p>This post looks long because I’ve put all the code I have so
that you can copy paste it. Skip to the <a href="#the-thing-that-does-the-diffing">good</a> part
if you already know how to test.</p>
<h2 id="do-the-npm">Do the npm</h2>
<p>If you want to test things with Puppeteer, you have to setup a thing for the
tests, a server that launches your site, and then Puppeteer to look
at that site. I have this in my <code class="language-plaintext highlighter-rouge">package.json</code> to wrangle these things:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">"devDependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"chai"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^4.1.2"</span><span class="p">,</span><span class="w">
</span><span class="nl">"mocha"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^5.0.0"</span><span class="p">,</span><span class="w">
</span><span class="nl">"puppeteer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^1.0.0"</span><span class="p">,</span><span class="w">
</span><span class="nl">"pixelmatch"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^4.0.2"</span><span class="p">,</span><span class="w">
</span><span class="nl">"polyserve"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^0.23.0"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>Explanation:</p>
<ul>
<li>I chose Mocha/Chai for testing because that’s what I’m used to. You can
literally use any other testing framework you’re comfortable with; I don’t think it matters.</li>
<li><a href="https://github.com/mapbox/pixelmatch"><code class="language-plaintext highlighter-rouge">Pixelmatch</code></a> is the thing that diffs
two images and tells you how many pixels they differ by. It’s super awesome 🏆.</li>
<li><a href="https://github.com/Polymer/polyserve"><code class="language-plaintext highlighter-rouge">Polyserve</code></a> is what I use as a local
server. You can use Python or Express or whatever you cool kids use. I’ll
point out in the code where it’s Polyserve specific (literally 2 lines), and you
can sub in your favourite server there.</li>
</ul>
<h2 id="set-up-your-test">Set up your test</h2>
<p>In order to tell Puppeteer to investigate your site, you need to:</p>
<ol>
<li>start a test suite</li>
<li>that sets up a local server</li>
<li>and in each test tells Puppeteer to do something.</li>
</ol>
<p>My setup looks like this:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">puppeteer</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">puppeteer</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">expect</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">chai</span><span class="dl">'</span><span class="p">).</span><span class="nx">expect</span><span class="p">;</span>
<span class="kd">const</span> <span class="p">{</span><span class="nx">startServer</span><span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">polyserve</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">describe</span><span class="p">(</span><span class="dl">'</span><span class="s1">👀 screenshots are correct</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">polyserve</span><span class="p">,</span> <span class="nx">browser</span><span class="p">,</span> <span class="nx">page</span><span class="p">;</span>
<span class="c1">// This is ran when the suite starts up.</span>
<span class="nx">before</span><span class="p">(</span><span class="k">async</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// This is where you would substitute your python or Express server or whatever.</span>
<span class="nx">polyserve</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">startServer</span><span class="p">({</span><span class="na">port</span><span class="p">:</span><span class="mi">4000</span><span class="p">});</span>
<span class="c1">// Create the test directory if needed. This and the goldenDir</span>
<span class="c1">// variables are global somewhere.</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">fs</span><span class="p">.</span><span class="nx">existsSync</span><span class="p">(</span><span class="nx">testDir</span><span class="p">))</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">mkdirSync</span><span class="p">(</span><span class="nx">testDir</span><span class="p">);</span>
<span class="c1">// And its wide screen/small screen subdirectories.</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">fs</span><span class="p">.</span><span class="nx">existsSync</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="nx">testDir</span><span class="p">}</span><span class="s2">/wide`</span><span class="p">))</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">mkdirSync</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="nx">testDir</span><span class="p">}</span><span class="s2">/wide`</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">fs</span><span class="p">.</span><span class="nx">existsSync</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="nx">testDir</span><span class="p">}</span><span class="s2">/narrow`</span><span class="p">))</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">mkdirSync</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="nx">testDir</span><span class="p">}</span><span class="s2">/narrow`</span><span class="p">);</span>
<span class="p">});</span>
<span class="c1">// This is ran when the suite is done. Stop your server here.</span>
<span class="nx">after</span><span class="p">((</span><span class="nx">done</span><span class="p">)</span> <span class="o">=></span> <span class="nx">polyserve</span><span class="p">.</span><span class="nx">close</span><span class="p">(</span><span class="nx">done</span><span class="p">));</span>
<span class="c1">// This is ran before every test. It's where you start a clean browser.</span>
<span class="nx">beforeEach</span><span class="p">(</span><span class="k">async</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">browser</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">puppeteer</span><span class="p">.</span><span class="nx">launch</span><span class="p">();</span>
<span class="nx">page</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">browser</span><span class="p">.</span><span class="nx">newPage</span><span class="p">();</span>
<span class="p">});</span>
<span class="c1">// This is ran after every test; clean up after your browser.</span>
<span class="nx">afterEach</span><span class="p">(()</span> <span class="o">=></span> <span class="nx">browser</span><span class="p">.</span><span class="nx">close</span><span class="p">());</span>
<span class="c1">// Wide screen tests!</span>
<span class="nx">describe</span><span class="p">(</span><span class="dl">'</span><span class="s1">wide screen</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">beforeEach</span><span class="p">(</span><span class="k">async</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">page</span><span class="p">.</span><span class="nx">setViewport</span><span class="p">({</span><span class="na">width</span><span class="p">:</span> <span class="mi">800</span><span class="p">,</span> <span class="na">height</span><span class="p">:</span> <span class="mi">600</span><span class="p">});</span>
<span class="p">});</span>
<span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">/view1</span><span class="dl">'</span><span class="p">,</span> <span class="k">async</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">takeAndCompareScreenshot</span><span class="p">(</span><span class="nx">page</span><span class="p">,</span> <span class="dl">'</span><span class="s1">view1</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">wide</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>
<span class="c1">// And your other routes, 404, etc.</span>
<span class="p">});</span>
<span class="c1">// Narrow screen tests are the same, but with a different viewport.</span>
<span class="nx">describe</span><span class="p">(</span><span class="dl">'</span><span class="s1">narrow screen</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">beforeEach</span><span class="p">(</span><span class="k">async</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">page</span><span class="p">.</span><span class="nx">setViewport</span><span class="p">({</span><span class="na">width</span><span class="p">:</span> <span class="mi">375</span><span class="p">,</span> <span class="na">height</span><span class="p">:</span> <span class="mi">667</span><span class="p">});</span>
<span class="p">});</span>
<span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">/view1</span><span class="dl">'</span><span class="p">,</span> <span class="k">async</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">takeAndCompareScreenshot</span><span class="p">(</span><span class="nx">page</span><span class="p">,</span> <span class="dl">'</span><span class="s1">view1</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">narrow</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>
<span class="c1">// And your other routes, 404, etc.</span>
<span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>
<p>You can test all sort of things here, by the way. Puppeteer lets you interact
with the page (click on buttons, links, etc), so maybe you want to trigger
different UI states before you screenshot them (like narrow view <em>but also</em> with
the navigation drawer opened).</p>
<h2 id="filling-in-the-blanks">Filling in the blanks</h2>
<p>All the heavy lifting (which isn’t very heavy tbh) is done in <code class="language-plaintext highlighter-rouge">takeAndCompareScreenshot</code>:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// - page is a reference to the Puppeteer page.</span>
<span class="c1">// - route is the path you're loading, which I'm using to name the file.</span>
<span class="c1">// - filePrefix is either "wide" or "narrow", since I'm automatically testing both.</span>
<span class="k">async</span> <span class="kd">function</span> <span class="nx">takeAndCompareScreenshot</span><span class="p">(</span><span class="nx">page</span><span class="p">,</span> <span class="nx">route</span><span class="p">,</span> <span class="nx">filePrefix</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// If you didn't specify a file, use the name of the route.</span>
<span class="kd">let</span> <span class="nx">fileName</span> <span class="o">=</span> <span class="nx">filePrefix</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">/</span><span class="dl">'</span> <span class="o">+</span> <span class="p">(</span><span class="nx">route</span> <span class="p">?</span> <span class="nx">route</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">index</span><span class="dl">'</span><span class="p">);</span>
<span class="c1">// Start the browser, go to that page, and take a screenshot.</span>
<span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">goto</span><span class="p">(</span><span class="s2">`http://127.0.0.1:4000/</span><span class="p">${</span><span class="nx">route</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
<span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">screenshot</span><span class="p">({</span><span class="na">path</span><span class="p">:</span> <span class="s2">`</span><span class="p">${</span><span class="nx">testDir</span><span class="p">}</span><span class="s2">/</span><span class="p">${</span><span class="nx">fileName</span><span class="p">}</span><span class="s2">.png`</span><span class="p">});</span>
<span class="c1">// Test to see if it's right.</span>
<span class="k">return</span> <span class="nx">compareScreenshots</span><span class="p">(</span><span class="nx">fileName</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="getting-the-golden-screenshots">Getting the golden screenshots</h3>
<p>This bit is easy. Make a different test suite (just make sure you don’t run it every time you
run your tests), and run the <code class="language-plaintext highlighter-rouge">page.goto</code> and <code class="language-plaintext highlighter-rouge">page.screenshot</code> lines for all
the routes you’re testing. I recommend doing the viewport dance too, to get both the
wide and narrow screen ones <em>for freeeeee</em> (I am using just the viewport
size here, because that’s how my app works. Puppeteer lets yo do <a href="https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pageemulateoptions">device emulation</a>
and all sorts of other goodness, so just read the docs). Put all these screenshots in a place; I put mine in
a folder called <code class="language-plaintext highlighter-rouge">test/screenshots-golden/</code>.</p>
<h3 id="the-thing-that-does-the-diffing">The thing that does the diffing</h3>
<p>This is the logic in <code class="language-plaintext highlighter-rouge">compareScreenshots</code>, and it’s basically straight
out of the <a href="https://github.com/mapbox/pixelmatch#nodejs"><code class="language-plaintext highlighter-rouge">Pixelmatch</code> docs</a>:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">compareScreenshots</span><span class="p">(</span><span class="nx">fileName</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">img1</span> <span class="o">=</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">createReadStream</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="nx">testDir</span><span class="p">}</span><span class="s2">/</span><span class="p">${</span><span class="nx">fileName</span><span class="p">}</span><span class="s2">.png`</span><span class="p">).</span><span class="nx">pipe</span><span class="p">(</span><span class="k">new</span> <span class="nx">PNG</span><span class="p">()).</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">parsed</span><span class="dl">'</span><span class="p">,</span> <span class="nx">doneReading</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">img2</span> <span class="o">=</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">createReadStream</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="nx">goldenDir</span><span class="p">}</span><span class="s2">/</span><span class="p">${</span><span class="nx">fileName</span><span class="p">}</span><span class="s2">.png`</span><span class="p">).</span><span class="nx">pipe</span><span class="p">(</span><span class="k">new</span> <span class="nx">PNG</span><span class="p">()).</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">parsed</span><span class="dl">'</span><span class="p">,</span> <span class="nx">doneReading</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">filesRead</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kd">function</span> <span class="nx">doneReading</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Wait until both files are read.</span>
<span class="k">if</span> <span class="p">(</span><span class="o">++</span><span class="nx">filesRead</span> <span class="o"><</span> <span class="mi">2</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
<span class="c1">// The files should be the same size.</span>
<span class="nx">expect</span><span class="p">(</span><span class="nx">img1</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="dl">'</span><span class="s1">image widths are the same</span><span class="dl">'</span><span class="p">).</span><span class="nx">equal</span><span class="p">(</span><span class="nx">img2</span><span class="p">.</span><span class="nx">width</span><span class="p">);</span>
<span class="nx">expect</span><span class="p">(</span><span class="nx">img1</span><span class="p">.</span><span class="nx">height</span><span class="p">,</span> <span class="dl">'</span><span class="s1">image heights are the same</span><span class="dl">'</span><span class="p">).</span><span class="nx">equal</span><span class="p">(</span><span class="nx">img2</span><span class="p">.</span><span class="nx">height</span><span class="p">);</span>
<span class="c1">// Do the visual diff.</span>
<span class="kd">const</span> <span class="nx">diff</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">PNG</span><span class="p">({</span><span class="na">width</span><span class="p">:</span> <span class="nx">img1</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="na">height</span><span class="p">:</span> <span class="nx">img2</span><span class="p">.</span><span class="nx">height</span><span class="p">});</span>
<span class="kd">const</span> <span class="nx">numDiffPixels</span> <span class="o">=</span> <span class="nx">pixelmatch</span><span class="p">(</span>
<span class="nx">img1</span><span class="p">.</span><span class="nx">data</span><span class="p">,</span> <span class="nx">img2</span><span class="p">.</span><span class="nx">data</span><span class="p">,</span> <span class="nx">diff</span><span class="p">.</span><span class="nx">data</span><span class="p">,</span> <span class="nx">img1</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="nx">img1</span><span class="p">.</span><span class="nx">height</span><span class="p">,</span>
<span class="p">{</span><span class="na">threshold</span><span class="p">:</span> <span class="mf">0.1</span><span class="p">});</span>
<span class="c1">// The files should look the same.</span>
<span class="nx">expect</span><span class="p">(</span><span class="nx">numDiffPixels</span><span class="p">,</span> <span class="dl">'</span><span class="s1">number of different pixels</span><span class="dl">'</span><span class="p">).</span><span class="nx">equal</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="nx">resolve</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="-its-all-worth-it">💯 It’s all worth it</h2>
<p>Now, when you run your tests (<code class="language-plaintext highlighter-rouge">mocha test/ --timeout 5000</code> in my case), you get
something like this:</p>
<p><img width="340" alt="10/10 passing tests" src="https://user-images.githubusercontent.com/1369170/35607089-624a2f28-0607-11e8-9448-0af2c40fe31a.png" /></p>
<p>And if it fails, you get an error and the number of pixels you’re off by.</p>
<h2 id="️">⭐️</h2>
<p>Now go on, navigate to all your routes and test your stuff, and thank me
with a photo of your dog.</p>
2017: another year in review2018-01-02T00:00:00+00:00https://meowni.ca/posts/2017-in-review<p>You can tell I hate writing year in reviews because this one is really, really
late. I tend to hate bragging, and I definitely hate introspective and, in
particular, I always think I am underperforming (and that’s fine). However, that’s usually not true,
and writing a year in review forces me to see the awesome things I did, so even if I did end up
underperforming, at least I can learn from that. That’s the whole point of post-mortems,
right?</p>
<p>As usual, here’s life as GitHub saw it. Red text is projects I’ve shipped, black text is
conferences I’ve spoken at. Technically I didn’t speak at Blinkon, but I spoke
<em>to</em> people at it so hey, counting it.</p>
<p><img alt="2017 contribution graph with project and conferences markers" src="/images/2017-review/github.png" /></p>
<p>The reason why this post mortem is important to me is that before writing it,
I literally thought this year was bad and I just “did fewer things”. But that’s
not actually true!</p>
<ul>
<li>
<p>I wrote less code than in 2016 (2713 vs 3153 contributions), but that’s totally
irrelevant? GitHub contributions are a fake idea, and I’m the first one
to tell you this, so I don’t know why I get worked up about them every year. I contributed
to weird branches (which don’t get counted), and did a lot of weird explorations that
obviously never got merged. I planned things. I wrote design documents. I reviewed
design documents. I formed strong opinions. I learnt Redux. Eat it, contributions graph.</p>
</li>
<li>
<p>I gave fewer talks than in 2016 (7 vs 8), but I enjoyed conferences more. I went
to a conference where I only hung out with badass women. I went to Railscamp, where
I had no wifi and canoed and wore a headlamp like a giant dork. I MC-ed Chrome Dev Summit
with Mariko, which was scary, and intimidating and incredibly fun.</p>
</li>
<li>
<p>I built more side projects (10 vs 9). One of them got
featured in <a href="https://www.theverge.com/2017/4/20/15331050/party-parrot-tweet-monica-dinculescu-terminal-self-care-github">The Verge</a>!</p>
</li>
<li>
<p>I worked fewer weekends (16 vs 28 days. Goal: 0). And I don’t mean on <em>work-work</em>,
I mean, <em>at all</em>. On the weekends I play Stardew Valley, or make pottery,
or knit, or watch an entire season of Riverdale <em>because I can</em>. Maybe this means
I’ll work on fewer side-things, and maybe this will hurt my career, but it will keep me happier, not burnt out, and less likely to murder my partner. And that’s p important.</p>
</li>
<li>
<p>I joined the Unicode Emoji committee. YES. Really. It took me a year of emails,
and even now I’m pretty sure I’m the least useful member out of the whole bunch,
but that’s ok, because I can get better!</p>
</li>
<li>
<p>I became broadly aware of a lot of technical things, but not necessarily
<em>deeply</em> aware of them. I am really confident I understand Web Components, emoji and web fonts
<em>really well</em>, but I still don’t really know how any of our polyfills work, or
how to fix a sizeable bug in Polymer, or what to do
about async/await and stuff. In the last couple of months of the year
I started learning more about <code class="language-plaintext highlighter-rouge">http/2 push</code> and <code class="language-plaintext highlighter-rouge">link rel=preload</code>, but I feel
it’s a broad sort of understanding. I don’t know yet what kind of person I want to
be: “I understand something <em>really</em> well in all its intimate aspects which makes me an expert”, or
“I understand many things well enough to have opinions and advice, but not well
enough to be an expert in any of them”. This is the thing I want to figure out this year.</p>
</li>
</ul>
<p>I learnt how to skateboard. I saw one of my favourite bands in concert.
I ordered a coffee entirely in Japanese. I learnt how to
make mugs and bowls and bottles, with my hands. I made new friends, and I didn’t piss off any
of my current ones (that I know of). I nuzzled dogs, and cats, and an otter.
I still didn’t spoil Star Wars. I turned 32.</p>
<p><img alt="2017 instagram top nine" src="/images/2017-review/instagram.png" /></p>
<p>I don’t do resolutions because they don’t really work for me, but I heard a
good one from a coworker: do 12 <strong>new</strong> things next year.</p>
<p>So, I will.</p>
<h3 id="️">❤️ </h3>
<p>// also available: <a href="../2016-in-review/">2016</a> and <a href="../a-year-in-review">2015</a> years in review,
that were actually on time.</p>
::part and ::theme, an ::explainer2017-12-18T00:00:00+00:00https://meowni.ca/posts/part-theme-explainer<p><strong>Updated May 18, 2020</strong></p>
<p>(get it? <code class="language-plaintext highlighter-rouge">::</code> ? I made a funny)</p>
<p><a href="https://meowni.ca/posts/shadow-dom/">Shadow DOM</a> is a spec that gives you DOM and style encapsulation. This is great for reusable <a href="https://meowni.ca/posts/web-components-with-otters/">web components</a>, as it reduces the ability of these components’ styles getting accidentally stomped over (the old <em>“I have a class called “button” and you have a class called “button”, now we both look busted”</em> problem), but it adds a barrier for styling and theming these components deliberately.</p>
<p>Since a lot has changed since the <a href="https://meowni.ca/posts/styling-the-dome/">last time</a> I talked about styling the Shadow DOM, I wanted to give you a quick update about what new specs were in the works! Please note that this spec <strong>isn’t</strong> quite final, which means that a) the syntax and capabilities will likely change and b) there isn’t a polyfill you can use for realsies.</p>
<hr />
<p>Ok, so. When talking about styling a component, there are usually two different problems you might want to solve:</p>
<p><strong>💇 Styling:</strong> I am using a third-party <code class="language-plaintext highlighter-rouge"><fancy-button></code> element on my site and I want this one to be blue</p>
<p><strong>🎨 Theming:</strong> I am using many third-party elements on my site, and some of them have a <code class="language-plaintext highlighter-rouge"><fancy-button></code>; I want all the <code class="language-plaintext highlighter-rouge"><fancy-button></code>s to be blue.</p>
<p>Here’s almost everything I know on this topic.</p>
<h2 id="a-trip-through-time">A trip through time</h2>
<p>There have been several previous attempts at solving this, some more successful than others. If you’ve read my <a href="https://meowni.ca/posts/styling-the-dome/">last</a> post about this, you’re already caught up. If you haven’t, here’s the deets:</p>
<ul>
<li>
<p>First came <code class="language-plaintext highlighter-rouge">:shadow</code> and <code class="language-plaintext highlighter-rouge">/deep/</code> (which have since been deprecated, and removed as of Chrome 60). These were shadow-piercing selectors that allowed you to target any node in an element’s Shadow DOM. Apart from being terribad for performance, they also required the user of an element to be intimately familiar with some random element’s implementation, which was unlikely and lead to them just breaking the whole element by accident</p>
</li>
<li>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/--*">Custom properties</a> allow you to create custom CSS properties that can be used throughout an app. In particular, they pierce the shadow boundary, which means they can be used for styling elements with a Shadow DOM:
If <code class="language-plaintext highlighter-rouge"><fancy-button></code> uses a <code class="language-plaintext highlighter-rouge">--fancy-button-background</code> property to control its background, then:</p>
</li>
</ul>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">fancy-button</span><span class="nf">#one</span> <span class="p">{</span> <span class="py">--fancy-button-background</span><span class="p">:</span> <span class="no">blue</span><span class="p">;</span> <span class="p">}</span> <span class="c">/* solves the 💇 problem and */</span>
<span class="nt">fancy-button</span> <span class="p">{</span> <span class="py">--fancy-button-background</span><span class="p">:</span> <span class="no">blue</span><span class="p">;</span> <span class="p">}</span> <span class="c">/* solves the 🎨 problem */</span>
</code></pre></div></div>
<ul>
<li>The problem with using just custom properties for styling/theming is that it places the onus on the element author to basically declare every possible styleable property as a custom property. As a result, <a href="http://tabatkins.github.io/specs/css-apply-rule/">@apply</a> was proposed, which basically allowed a custom property to hold an entire ruleset (a bag of other properties!). <a href="https://twitter.com/tabatkins">Tab Atkins</a> has a very good <a href="https://www.xanthir.com/b4o00">post</a> as to why this approach was abandoned, but the tl;dr; is that it interacted pretty poorly with pseudo classes and elements (like <code class="language-plaintext highlighter-rouge">:focus</code>, <code class="language-plaintext highlighter-rouge">:hover</code>, <code class="language-plaintext highlighter-rouge">::placeholder</code> for input), which still meant the element author would have to define a looooot of these bags of properties to be used in the right places.</li>
</ul>
<h2 id="and-now-something-different-but-the-same">And now: something different but the same</h2>
<p>The current new proposal is <a href="https://drafts.csswg.org/css-shadow-parts-1/">::part</a> (and possibly later, <code class="language-plaintext highlighter-rouge">::theme</code>), a set of pseudo-elements that allow you to style inside a shadow tree, from outside of that shadow tree. Unlike <code class="language-plaintext highlighter-rouge">:shadow</code> and <code class="language-plaintext highlighter-rouge">/deep/</code>, they don’t allow you to style arbitrary elements inside a shadow tree: they only allow you to style elements that an author has tagged as being eligible for styling. They’ve already gone through the CSS working group and were blessed, and were brought up at TPAC at a Web Components session, so we’re confident they’re both the right approach, and highly likely to be implemented as a spec by all browsers, though there is some discussion of the exact selector syntax still going on.</p>
<h2 id="how-part-works">How ::part works</h2>
<p>You can specify a “styleable” part on any element in your shadow tree:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><x-foo></span>
#shadow-root
<span class="nt"><div</span> <span class="na">part=</span><span class="s">"some-box"</span><span class="nt">><span></span>...<span class="nt"></span></div></span>
<span class="nt"><input</span> <span class="na">part=</span><span class="s">"some-input"</span><span class="nt">></span>
<span class="nt"><div></span>...<span class="nt"></div></span> <span class="c"><!-- not styleable --></span>
<span class="nt"></x-foo></span>
</code></pre></div></div>
<p>If you’re in a document that has an <code class="language-plaintext highlighter-rouge"><x-foo></code> in it, then you can style those parts with:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">x-foo</span><span class="nd">::part</span><span class="o">(</span><span class="nt">some-box</span><span class="o">)</span> <span class="p">{</span> <span class="err">...</span> <span class="p">}</span>
</code></pre></div></div>
<p>You <strong>can</strong> use other pseudo elements or selectors (that were not explicitly exposed as shadow parts), so both of these work:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">x-foo</span><span class="nd">::part</span><span class="o">(</span><span class="nt">some-box</span><span class="o">)</span><span class="nd">:hover</span> <span class="p">{</span> <span class="err">...</span> <span class="p">}</span>
<span class="nt">x-foo</span><span class="nd">::part</span><span class="o">(</span><span class="nt">some-input</span><span class="o">)</span><span class="nd">::placeholder</span> <span class="p">{</span> <span class="err">...</span> <span class="p">}</span>
</code></pre></div></div>
<p>You <strong>cannot</strong> select inside of those parts, so this <strong>doesn’t</strong> work:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">x-foo</span><span class="nd">::part</span><span class="o">(</span><span class="nt">some-box</span><span class="o">)</span> <span class="nt">span</span> <span class="p">{</span> <span class="err">...</span> <span class="p">}</span> <span class="nt">nor</span>
<span class="nt">x-foo</span><span class="nd">::part</span><span class="o">(</span><span class="nt">some-box</span><span class="o">)</span><span class="nd">::part</span><span class="o">(</span><span class="nt">some-other-thing</span><span class="o">)</span> <span class="p">{</span> <span class="err">...</span> <span class="p">}</span>
</code></pre></div></div>
<p>You <strong>cannot</strong> style this part more than one level up if you don’t forward it. So without any extra work, if you have an element that contains an <code class="language-plaintext highlighter-rouge">x-foo</code> like this:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><x-bar></span>
#shadow-root
<span class="nt"><x-foo></x-foo></span>
<span class="nt"></x-bar></span>
</code></pre></div></div>
<p>You <strong>cannot</strong> select and style the <code class="language-plaintext highlighter-rouge">x-foo</code>’s part like this:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">x-bar</span><span class="nd">::part</span><span class="o">(</span><span class="nt">some-box</span><span class="o">)</span> <span class="p">{</span> <span class="err">...</span> <span class="p">}</span>
</code></pre></div></div>
<h3 id="forwarding-parts">Forwarding parts</h3>
<p>You <strong>can</strong> explicitly forward a child’s part to be styleable outside of the parent’s shadow tree with the <code class="language-plaintext highlighter-rouge">exportparts</code> attribute. So in the previous example, to allow the <code class="language-plaintext highlighter-rouge">some-box</code> part to be styleable by <code class="language-plaintext highlighter-rouge">x-bar</code>’s parent, it would have to be exposed:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><x-bar></span>
#shadow-root
<span class="nt"><x-foo</span> <span class="na">exportparts=</span><span class="s">"some-box"</span><span class="nt">></x-foo></span>
<span class="nt"></x-bar></span>
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">exportparts</code> forwarding syntax has options a-plenty. 🙏 Feel free to skip these if
you’re not interested in the minutiae of the syntax!</p>
<ul>
<li>
<p><code class="language-plaintext highlighter-rouge">exportparts="some-box some-input"</code>: explicitly forward <code class="language-plaintext highlighter-rouge">x-foo</code>’s parts that you know about (i.e. <code class="language-plaintext highlighter-rouge">some-box</code> and <code class="language-plaintext highlighter-rouge">some-input</code>) as they are. These selectors <strong>would</strong> match:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">x-bar</span><span class="nd">::part</span><span class="o">(</span><span class="nt">some-box</span><span class="o">)</span> <span class="p">{</span> <span class="err">...</span> <span class="p">}</span>
<span class="nt">x-bar</span><span class="nd">::part</span><span class="o">(</span><span class="nt">some-input</span><span class="o">)</span> <span class="p">{</span> <span class="err">...</span> <span class="p">}</span>
</code></pre></div> </div>
</li>
<li>
<p><code class="language-plaintext highlighter-rouge">exportparts="some-input: bar-input"</code>: explicitly forward (some) of <code class="language-plaintext highlighter-rouge">x-foo</code>’s parts (i.e. <code class="language-plaintext highlighter-rouge">some-input</code>) but rename them. These selectors <strong>would</strong> match:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">x-bar</span><span class="nd">::part</span><span class="o">(</span><span class="nt">bar-input</span><span class="o">)</span> <span class="p">{</span> <span class="err">...</span> <span class="p">}</span>
</code></pre></div> </div>
<p>These selectors would <strong>not</strong> match:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">x-bar</span><span class="nd">::part</span><span class="o">(</span><span class="nt">some-box</span><span class="o">)</span> <span class="p">{</span> <span class="err">...</span> <span class="p">}</span>
<span class="nt">x-bar</span><span class="nd">::part</span><span class="o">(</span><span class="nt">some-input</span><span class="o">)</span> <span class="p">{</span> <span class="err">...</span> <span class="p">}</span>
<span class="nt">x-bar</span><span class="nd">::part</span><span class="o">(</span><span class="nt">bar-box</span><span class="o">)</span> <span class="p">{</span> <span class="err">...</span> <span class="p">}</span>
</code></pre></div> </div>
</li>
<li>
<p>You <em>can</em> combine these, as well as add a part to <code class="language-plaintext highlighter-rouge">x-foo</code> itself (<code class="language-plaintext highlighter-rouge">some-foo</code> below. This means “style this particular <code class="language-plaintext highlighter-rouge">x-foo</code>, but not the other one, if you had more):</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><x-bar></span>
#shadow-root
<span class="nt"><x-foo</span> <span class="na">part=</span><span class="s">"some-foo"</span> <span class="na">exportparts=</span><span class="s">"some-input: bar-input"</span><span class="nt">></x-foo></span>
<span class="nt"></x-bar></span>
</code></pre></div> </div>
</li>
</ul>
<h2 id="the-all-buttons-in-this-app-should-be-blue--theming-problem">The “all buttons in this app should be blue” 🎨 theming problem</h2>
<p>Given the above prefixing rules, to style all inputs in a document at once, you need to
Ensure that all elements correctly forward their parts and
Select all their parts.</p>
<p>So given this shadow tree:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><submit-form></span>
#shadow-root
<span class="nt"><x-form</span> <span class="na">exportparts=</span><span class="s">"some-input some-box"</span><span class="nt">></span>
#shadow-root
<span class="nt"><x-bar</span> <span class="na">exportparts=</span><span class="s">"some-input some-box"</span><span class="nt">></span>
#shadow-root
<span class="nt"><x-foo</span> <span class="na">exportparts=</span><span class="s">"some-input some-box"</span><span class="nt">></x-foo></span>
<span class="nt"></x-bar></span>
<span class="nt"></x-form></span>
<span class="nt"></submit-form></span>
<span class="nt"><x-form></x-form></span>
<span class="nt"><x-bar></x-bar></span>
</code></pre></div></div>
<p>You can style all the inputs with:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">:root::part</span><span class="o">(</span><span class="nt">some-input</span><span class="o">)</span> <span class="p">{</span> <span class="err">...</span> <span class="p">}</span>
</code></pre></div></div>
<p>👉 This is a lot of effort on the element author, but easy on the theme user.</p>
<p>If you hadn’t forwarded them with the same name and <code class="language-plaintext highlighter-rouge">some-input</code> was used at every level of the app (the non contrived example is just an <code class="language-plaintext highlighter-rouge"><a></code> tag that’s used in many shadow roots), then you’d have to write:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">:root::part</span><span class="o">(</span><span class="nt">form-bar-foo-some-input</span><span class="o">),</span>
<span class="nd">:root::part</span><span class="o">(</span><span class="nt">bar-foo-some-input</span><span class="o">,</span>
<span class="nd">:root::part</span><span class="o">(</span><span class="nt">foo-some-input</span><span class="o">),</span>
<span class="nd">:root::part</span><span class="o">(</span><span class="nt">some-input</span><span class="o">)</span> <span class="p">{</span> <span class="err">...</span> <span class="p">}</span>
</code></pre></div></div>
<p>👉 This is a lot of effort on the theme user, but easy on the element author.</p>
<p>Both of these examples show that if an element author forgot to forward a part, then the app can’t be themed correctly.</p>
<h2 id="how-theme-might-work">How ::theme might work</h2>
<p><code class="language-plaintext highlighter-rouge">::theme</code> is another pseudoelement originally proposed to pair with <code class="language-plaintext highlighter-rouge">::part</code>. It matches any parts with that name, anywhere in the document. This means that if you hadn’t forwarded any parts, i.e.:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><x-bar></span>
#shadow-root
<span class="nt"><x-foo></x-foo></span>
<span class="nt"><x-foo></x-foo></span>
<span class="nt"><x-foo></x-foo></span>
<span class="nt"></x-bar></span>
</code></pre></div></div>
<p>You could style all of the inputs in x-bar with:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">x-bar</span><span class="nd">::theme</span><span class="o">(</span><span class="nt">some-input</span><span class="o">)</span> <span class="p">{</span> <span class="err">...</span> <span class="p">}</span>
</code></pre></div></div>
<p>This can go arbitrarily deep in the shadow tree. So, no matter how deeply nested they are, you could style all the inputs with <code class="language-plaintext highlighter-rouge">part="some-input"</code> in the app with:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">:root::theme</span><span class="o">(</span><span class="nt">some-input</span><span class="o">)</span> <span class="p">{</span> <span class="err">...</span> <span class="p">}</span>
</code></pre></div></div>
<h2 id="demo">Demo</h2>
<p>As mentioned before, this spec is still in the works and we don’t have a shim that you can use in production. Hell, this shim isn’t even guaranteed to work for all the cases that should work according to the spec, so you should take this code with an enormous iceberg of salt. This is a <a href="https://part-theme.glitch.me/">demo</a> that illustrates styling and theming a bunch of vanilla custom elements in a form.</p>
<p>Some notes:</p>
<ul>
<li>
<p>this shim is meant for a demo prototype of the (still in the works) API. it is a very very very very rough shim, which means its performance is badly in the weeds (don’t use it in production. don’t use it for anything you care about)</p>
</li>
<li>
<p>it probably has bugs and doesn’t implement the spec 100%, and nobody will fix these bugs. Again, this shim wasn’t ever meant to be used for realsies</p>
</li>
<li>
<p>the shim is implemented as a mixin, which means you can only use <code class="language-plaintext highlighter-rouge">::part</code> or <code class="language-plaintext highlighter-rouge">::theme</code> inside of a custom element using that mixin (see <code class="language-plaintext highlighter-rouge">another-form.js</code>)</p>
</li>
</ul>
Chrome extensions for quick site redesigns2017-09-20T00:00:00+00:00https://meowni.ca/posts/extensions<p>There’s this thing I hate about the modern web which is that sites are rarely
one giant html file filled with goodies. You can’t just “run a site” locally.
You need an <code class="language-plaintext highlighter-rouge">npm</code> or a <code class="language-plaintext highlighter-rouge">gulp</code> step or a <code class="language-plaintext highlighter-rouge">docker</code> if you’re lucky. And probably
a local server, but not the one you have installed. Which, I mean,
makes sense, because modern web sites are big and powerful and have complicated
front-ends and <em>do</em> more things than a giant html file would. But
it also kind of sucks because the build ceremonial sacrifices can be a bit overwhelming.
Maybe you just want to see what the links would look like if they didn’t have
underlines. Maybe you want to change the fonts. Maybe you’re never even
going to ship these changes, you just want to get a feel for them.</p>
<p>Boy, have I got an idea for you: Chrome extensions. Hear me out. A Chrome extension
is a bit of code that runs on a specific page (or set of pages). It can be anything
you want. In particular, it can be a CSS stylesheet.</p>
<p>Then, re-theming a site is just a matter of installing this Chrome extension. If
you want to share it with people, you can just zip it up and send it around.
It’s obviously not production ready, but it’s amaaaaazing for prototyping.</p>
<p>I made a <a href="https://glitch.com/edit/#!/chrome-css-extension">glitch</a> project
that gets you started with writing a Chrome extension that injects a CSS stylesheet.
There’s only one thing you need to know: this stylesheet is a <em>User Agent</em>
stylesheet, which means it has the lowest specificity. So some of its styles
won’t get applied unless you slap some <code class="language-plaintext highlighter-rouge">!important</code>s on it (or have extra
specific selectors). Or, if you have an ID, you can do my favourite CSS hack ever
that I learnt from <a href="https://twitter.com/dassurma">Surma</a> and will take to the grave:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">#foo#foo</span> <span class="p">{</span>
<span class="c">/* this is a really winner #foo selector */</span>
<span class="nl">color</span><span class="p">:</span> <span class="no">red</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<hr />
<p>That’s it! If you want to see an example of such an extension in the wild, I made <a href="https://github.com/notwaldorf/picasso">picasso</a>,
which is just a pretty Google Calendar theme. Originally it was just a local
extension I kept on my machine, but eventually I published it because I
realized other people may want to give their calendar a bubble bath. Anyway, happy retheming!</p>
Shadow DOM: fast and encapsulated styles2017-08-11T00:00:00+00:00https://meowni.ca/posts/shadow-dom<p><a href="https://developers.google.com/web/fundamentals/getting-started/primers/shadowdom">Shadow DOM</a> is a fairly recent-ish spec that gives you DOM tree encapsulation – it’s one of the superhero lions in the Voltron of specs called “Web Components”. <a href="https://www.webcomponents.org/introduction">Web Components</a> let you create reusable, self-contained components in JavaScript; the Shadow DOM bit makes sure that the CSS and markup you bundle with your implementation is encapsulated, hiding the implementation details of your element.</p>
<p>The idea of encapsulation isn’t new – most programming languages have a way to define “private” bits of code – variables or methods that are irrelevant to the user of that object and make the element work. Messing with them usually voids the contract and breaks the guarantee that the element will continue to work. In these languages you could, instead, use a global variable or method for everything. It’s not a question of whether it will work (it will), but whether it will work over time, in a large code base (it won’t). You know it won’t.</p>
<p>On the web, there’s two kinds of encapsulation we might want: style encapsulation (an element’s styles don’t leak outside) and DOM encapsulation (an element’s internal implementation isn’t visible). This post talks about style encapsulation; tune in soon for the second half of the story – the DOM encapsulation!</p>
<p>Whew, ok then. So then why is CSS encapsulation so hard? And what’s the fastest way to get it?</p>
<hr />
<h2 id="tools-to-the-rescue">Tools to the rescue!</h2>
<p>🙏 Before you set me on fire on Twitter, hear this: the next paragraph isn’t a criticism of CSS (which I think is the greatest tool for authoring styles) nor a criticism of the tools we use (which I think fill real gaps we have), but a criticism of the standards process itself.</p>
<p>I have a theory that developers will put up with too much when it comes to writing CSS. For a while there, CSS wasn’t moving forward, so people started using tools to get around that. We didn’t have variables or mixins, so we started using preprocessors. We didn’t have style encapsulation, so we started naming things “the right way” with BEM, so that we didn’t accidentally stomp over each other’s styles. We wanted to be able to author CSS from inside a JavaScript component, so we started using CSS-in-JS. We needed all these tools, because “the platform” (read: the browsers that be) wasn’t there, and building these tools showed that there was a need to move forward. For style encapsulation, Shadow DOM <strong>is</strong> the platform moving forward.</p>
<p>The unsatisfying part of the web is that you don’t have these problems when you build a one page site or app – you have control over your 17 shades of slightly different blue and your custom build pipeline. But when you have big projects, with weird architectures, targeting different platforms and written across different teams, you end up spending a lot of time just setting up infrastructure and build configurations, which kind of sucks.</p>
<h2 id="existing-scoping-approaches">Existing scoping approaches</h2>
<p>So now that you (maybe) believe me that style encapsulation is a good thing, let’s talk about the bunch of ways in which you can get various degrees of it. They basically come in two flavours: encapsulation by convention or encapsulation with buy-in. Here they are (in my opinion), from least to most effective:</p>
<h3 id="1-better-naming-strategies">1. Better naming strategies</h3>
<p>“<em>Name your stuff better</em>” works if you have control over the things you are naming. But if you already do, then you probably don’t need style encapsulation in the first place. You can just…not…do the bad things and the stomping. The problem is that if you’re building a third party widget (say, a fancy date picker that everyone in the universe will have to use), or if you’re building something as part of a large team, you have to be very, very careful not to name it anything that anyone out there might ever call it. Not very scientific.</p>
<p class="chunk">
👍 It’s really easy and doesn’t need tools.<br /><br />
👎 It’s really hard if you don’t have tools to enforce it. And doesn’t really work.
</p>
<h3 id="2-iframe">2. <iframe></h3>
<p>Ugh, you know it works. Iframes are this special magical portal that teleports any piece of HTML into your piece of HTML, while keeping it wrapped in a safety bubble. But you can’t resize them easily. Or scroll nicely. Or pretend they’re not a teleported piece of code wrapped in a safety bubble. I didn’t even have to doctor this screenshot, it’s real life:</p>
<p><img alt="google search suggestions for 'iframes are'" src="/images/2017-08-11/iframes.png" /></p>
<p class="chunk">
👍 It’s the most encapsulation and abstraction you will ever get on the web.<br /><br />
👎 It’s an iframe.
</p>
<h3 id="3-css-modules">3. CSS modules</h3>
<p><a href="https://m.alphasights.com/css-evolution-from-css-sass-bem-css-modules-to-styled-components-d4c1da3a659b">CSS Modules</a> are another approach to faking style encapsulation. It’s basically a smart way of automating BEM, so that you don’t have to worry about choosing the unique class names – there’s a tool that does it for you! It works pretty well, since it prevents any potential name collisions you’ve had with BEM, but at the end of the day, it’s not <em>actually</em> style encapsulation. There’s nothing stopping you from styling any bit of the DOM tree, which means it’s not a very satisfactory answer if you’re in the business of vending, or using, robust third party components.</p>
<h3 id="4-css-in-js">4. CSS-in-JS</h3>
<p><a href="https://medium.freecodecamp.com/css-in-javascript-the-future-of-component-based-styling-70b161a79a32">CSS-in-JS</a> is a new approach that lets you author CSS literally in JavaScript. Then, this JavaScript is basically transmogrified into a style, which means that that style is sort of encapsulated – it’s local to that element, and hard to stomp over. There’s several ways to do this, some better than others:</p>
<h4 id="directly-setting-the-style-as-an-attribute">Directly setting the style as an attribute</h4>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>someElement.style.marginLeft = ‘20px’
</code></pre></div></div>
<p>This is the worst of all the worlds because the CSS parser can do way fewer optimizations and caching than if you used class names, for example (see <a href="https://twitter.com/notwaldorf/status/859636431974739968">a benchmark</a>).</p>
<h4 id="embedding-css-style-strings-in-your-js-output">Embedding CSS style strings in your JS output</h4>
<p>Something like <code class="language-plaintext highlighter-rouge"><div style=”...”></code> is still pretty terrible for performance. Browsers (or at least Chrome), do a looooooot of string conversions in this case, which means it at least doubles your memory footprint, because the same string has to live both in V8 and Blink. Here’s what happens behind the scenes:</p>
<ul>
<li>Take the JS off the wire, in whatever encoding your page is in</li>
<li>Turn it into whatever encoding V8 prefers, for super optimal memory compactness</li>
<li>Scan the JavaScript string</li>
<li>Parse the JavaScript string</li>
<li>Turn it into an internal string for the DOM when you want to apply the styles</li>
<li>Potentially re-encode it if you’re unlucky</li>
<li>Take the internal string, pass it to Blink (string copies ahoy!)</li>
<li>Blink passes it to the CSS parser, which turns it into styles</li>
</ul>
<h4 id="compiling-out-your-css">Compiling out your CSS</h4>
<p>Like, into a separate resource, and then applying styles via classes. This works really well, since you’ve used the browser as it wanted to be used. In comparison to the previous case, for a regular <code class="language-plaintext highlighter-rouge"><style></code> in a CSS stylesheet, the browser has the same string and just passes it around:</p>
<ul>
<li>Take the CSS off the wire into Blink</li>
<li>Tokenize it</li>
<li>Build a DOM tree with the string as a text node</li>
<li>Parse the text node</li>
<li>Pass it to the CSS parser, which turns it into styles</li>
</ul>
<p class="chunk">
👍 Managing a giant amount of styles is nice. Style encapsulation is nice. It works extremely well if you’re using a framework that works well with this.<br /><br />
👎 There’s <a href="https://github.com/MicheleBertoli/css-in-js">a million</a> ways to do this, and it’s really overwhelming if you are new to it. This approach tends to also be married to a framework, which makes sharing components hard -- both the user and the author of a component need to agree on <b>both</b> the framework and the css-in-js style, which isn’t always possible.
</p>
<h3 id="4-shadow-dom">4. Shadow DOM</h3>
<p>This is a cheap move: you know this article is about the Shadow DOM, and I left it until the end because I obviously think it’s the best. Shadow DOM was literally built to solve the problem of style and DOM encapsulation. It does the same thing that <code class="language-plaintext highlighter-rouge"><input></code> and <code class="language-plaintext highlighter-rouge"><video></code> elements have been doing for years (hiding their dirty laundry) but in a way that browsers can optimize around.</p>
<p>The reason for that is that browsers have a special <strong>style resolver</strong> for Shadow DOM trees. Apart from being regular CSS that the browser already knows how to optimize, the CSS inside shadow DOM trees only applies inside that element. This means that changing a class name or style inside of a shadow root won’t affect everything outside it. Since you don’t have to consider the rest of the world, this means style resolution and application is much faster.</p>
<p>The same argument can be made for element authors – since you know that everything inside of your element can’t leak outside, the implementation is much simpler. You don’t have to think about <em>the rest</em> of the world. You only have to consider your element’s public API, and its implementation.</p>
<p>Before you complain that using a Shadow DOM and Web Components means that it absolutely requires JavaScript: this is true. But if you’re in a big team, building the kind of big app where you’re looking to style encapsulation as a solution for your CSS bowl of spaghetti, I’m pretty sure you’re already using JavaScript. And the community has been exploring <a href="https://github.com/skatejs/ssr">solutions</a> to server-side rendering Shadow DOM anyway. Tradeoffs be tradeoffs, and this seems like an easy one.</p>
<p class="chunk">
👍 We’ve been complaining that nothing in CSS was helping with style encapsulation and this is <i>literally</i> the platform’s answer to that problem.<br /><br />
👎 Because it’s a new spec, it’s suffering from some growing pains. On older browsers you need a <a href="https://github.com/webcomponents/shadycss">polyfill</a>. If you want reusable elements that are also highly customizable, this style encapsulation might get in the way right now. Thankfully, good people are already working on that. <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/--*">Custom properties</a> are a new spec meant to address this, and the <a href="https://tabatkins.github.io/specs/css-shadow-parts/">new proposal</a> for theming custom elements is now an <a href="https://twitter.com/tabatkins/status/893376459091390464">editor's draft</a>!
</p>
<hr />
<p>The zen of web development is a small page – reusable components, not a lot of code, no wheels reinvented. Encapsulated styles are better for you as a developer (code can be simpler), and better for you as a platform (code can be faster). And without external tools or iframe nightmares, the only way to get this is Shadow DOM.</p>
PWAs with Polymer: a checklist2017-07-26T00:00:00+00:00https://meowni.ca/posts/polymer-pwa-checklist<style>
.content img:not(.emoji) {
border: 1px solid #efefef;
margin: 24px 0;
}
</style>
<p>The Meownica Web App Workflow™ goes like this:</p>
<ol>
<li>Write bad code until the file is too long</li>
<li>Refactor that code into some web components</li>
<li>Repeat steps 1-2 until done</li>
<li>Realize you forgot to do the PWA dance, so your app is scoring 45 on <a href="https://chrome.google.com/webstore/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk?hl=en">Lighthouse</a></li>
<li>Make it into a PWA by doing basically the same steps every time.</li>
</ol>
<p>I’m not joking about step 5. It’s all a bunch of fairly simple boilerplate and
party tricks, that I copy paste from a couple of apps. This time I decided to make
them into a checklist. This checklist is keen on the <code class="language-plaintext highlighter-rouge">polymer cli</code>, because I
usually write apps that use Polymer. If you don’t, you can replace the <code class="language-plaintext highlighter-rouge">polymer cli</code> with your favourite bundler/service-worker generator!</p>
<p>If you just want the checklist, <a href="https://meowni.ca/posts/polymer-pwa-checklist/#checklist">here</a> it is. If you want to see <em>how</em> I made this checklist and how the <a href="https://chrome.google.com/webstore/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk?hl=en">Lighthouse</a> score improved as I checked more items off, jump to the <a href="#step-by-step">case-study</a>!</p>
<ul id="markdown-toc">
<li><a href="#checklist" id="markdown-toc-checklist">Checklist</a></li>
<li><a href="#step-by-step" id="markdown-toc-step-by-step">Step by step</a> <ul>
<li><a href="#1--add-a-manifestjson" id="markdown-toc-1--add-a-manifestjson">1. 📝 Add a manifest.json</a></li>
<li><a href="#2--bundle-with-the-polymer-cli" id="markdown-toc-2--bundle-with-the-polymer-cli">2. 🏃 Bundle with the Polymer CLI</a></li>
<li><a href="#3--add-a-service-worker" id="markdown-toc-3--add-a-service-worker">3. 🤖 Add a Service Worker</a></li>
<li><a href="#4--fix-first-paint" id="markdown-toc-4--fix-first-paint">4. 🎨 Fix first paint</a></li>
</ul>
</li>
<li><a href="#-" id="markdown-toc--">🆗 🆒</a></li>
</ul>
<h2 id="checklist">Checklist</h2>
<p><input type="checkbox" /> generate icons (sizes: 48x48, 96x96, 144x144, 192x192, 512x512) <a href="https://github.com/PolymerLabs/indie-catalog/tree/master/icons">[example]</a><br />
<input type="checkbox" /> add a <code class="language-plaintext highlighter-rouge">manifest.json</code> <a href="https://github.com/Polymer/shop/blob/master/manifest.json">[example]</a><br />
<input type="checkbox" /> add the rest of the manifesty things to your index.html <a href="https://github.com/PolymerLabs/indie-catalog/blob/master/index.html#L14">[example]</a><br />
<input type="checkbox" /> add the <a href="https://github.com/Polymer/polymer-cli">polymer cli</a>: <code class="language-plaintext highlighter-rouge">npm install -g polymer-cli</code><br />
<input type="checkbox" /> add a <code class="language-plaintext highlighter-rouge">polymer.json</code> <a href="https://github.com/Polymer/shop/blob/master/polymer.json">[example]</a><br />
<input type="checkbox" /> run <code class="language-plaintext highlighter-rouge">polymer build</code><br />
<input type="checkbox" /> register your Service Worker <a href="https://github.com/PolymerLabs/indie-catalog/blob/master/index.html#L102">[example]</a>. If you have a complicated app setup or caching strategy, you might want to generate a <a href="https://github.com/Polymer/shop/blob/master/sw-precache-config.js"><code class="language-plaintext highlighter-rouge">sw-precache-config.js</code></a> file.<br />
<input type="checkbox" /> add fallback content while your main element is updating <a href="https://github.com/PolymerLabs/indie-catalog/blob/master/index.html#L84">[example]</a>. As a general rule, I try to match this fallback content very closely to what the first paint of the element will actually be, so that there’s no visual jank<br />
<input type="checkbox" /> make sure that your page renders something without JavaScript <a href="https://github.com/PolymerLabs/indie-catalog/blob/master/index.html#L90">[example]</a></p>
<p>Provided your app isn’t outrageously big (think: the only thing that will make
loading 10MB of JavaScript up front better, is not loading 10MB of JavaScript), this should help put you somewhere in the green scores on Lighthouse.</p>
<h2 id="step-by-step">Step by step</h2>
<p>So, here’s the post-game analysis of what I did to make <a href="https://github.com/polymerlabs/indie-catalog">indie-catalog</a> into a PWA with a pretty decent Lighthouse score. I didn’t take it all the way to 💯, because the last 5-10 points always end
up being very app specific, and that kind of sorcery is best left for a different blog post.</p>
<p>It doesn’t particularly matter what my app does – you can think of it as a generic Polymer 2.0 app, with a bunch of Polymer elements, that I have done nothing special to. It doesn’t have a Service
Worker, it doesn’t lazy load anything, it doesn’t bundle or minify any of the loaded code. Its Lighthouse
score is an absolute tragedy (minus that a11y score 🙌):</p>
<p><img alt="initial Lighthouse score" src="/images/2017-07-26/before-score.png" /></p>
<p>The PWA section details point to the very straight forward problem of “you have no Service Worker, what did you expect”. TBH, exactly this.
<img alt="initial Lighthouse PWA section" src="/images/2017-07-26/before-pwa.png" /></p>
<p>Performance wise, the app is really slow. Because it doesn’t minify any if its sources, it
has to download a lot of things, a lot of times, which is a horrifying experience on 3G:
<img alt="initial Lighthouse performance section" src="/images/2017-07-26/before-perf.png" /></p>
<h3 id="1--add-a-manifestjson">1. 📝 Add a manifest.json</h3>
<p>This is easy Lighthouse points. This is a <code class="language-plaintext highlighter-rouge">manifest.json</code> skeleton that I use; replace
your app name and theme colour:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"your-name"</span><span class="p">,</span><span class="w">
</span><span class="nl">"short_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"shorter"</span><span class="p">,</span><span class="w">
</span><span class="nl">"start_url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/"</span><span class="p">,</span><span class="w">
</span><span class="nl">"display"</span><span class="p">:</span><span class="w"> </span><span class="s2">"standalone"</span><span class="p">,</span><span class="w">
</span><span class="nl">"theme_color"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#fbbc05"</span><span class="p">,</span><span class="w">
</span><span class="nl">"background_color"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#fbbc05"</span><span class="p">,</span><span class="w">
</span><span class="nl">"icons"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"src"</span><span class="p">:</span><span class="w"> </span><span class="s2">"icons/icon-192x192.png"</span><span class="p">,</span><span class="w">
</span><span class="nl">"sizes"</span><span class="p">:</span><span class="w"> </span><span class="s2">"192x192"</span><span class="p">,</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"image/png"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"src"</span><span class="p">:</span><span class="w"> </span><span class="s2">"icons/icon-512x512.png"</span><span class="p">,</span><span class="w">
</span><span class="nl">"sizes"</span><span class="p">:</span><span class="w"> </span><span class="s2">"512x512"</span><span class="p">,</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"image/png"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>Then, load it in your <code class="language-plaintext highlighter-rouge">index.html</code>, along with this other absolutely fantastic
platform-specific copy pasta. I’m sure there’s a script out there that
does it for you, but I’ve become so good at copy pasting it that it really doesn’t
matter. Also, it’s not like you do it more than once an app:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><link</span> <span class="na">rel=</span><span class="s">"icon"</span> <span class="na">href=</span><span class="s">"icons/favicon.ico"</span><span class="nt">></span>
<span class="c"><!-- See https://goo.gl/OOhYW5 --></span>
<span class="nt"><link</span> <span class="na">rel=</span><span class="s">"manifest"</span> <span class="na">href=</span><span class="s">"manifest.json"</span><span class="nt">></span>
<span class="c"><!-- See https://goo.gl/qRE0vM --></span>
<span class="nt"><meta</span> <span class="na">name=</span><span class="s">"theme-color"</span> <span class="na">content=</span><span class="s">"#fbbc05"</span><span class="nt">></span>
<span class="c"><!-- Add to homescreen for Chrome on Android. Fallback for manifest.json --></span>
<span class="nt"><meta</span> <span class="na">name=</span><span class="s">"mobile-web-app-capable"</span> <span class="na">content=</span><span class="s">"yes"</span><span class="nt">></span>
<span class="nt"><meta</span> <span class="na">name=</span><span class="s">"application-name"</span> <span class="na">content=</span><span class="s">"indie-catalog"</span><span class="nt">></span>
<span class="c"><!-- Add to homescreen for Safari on iOS --></span>
<span class="nt"><meta</span> <span class="na">name=</span><span class="s">"apple-mobile-web-app-capable"</span> <span class="na">content=</span><span class="s">"yes"</span><span class="nt">></span>
<span class="nt"><meta</span> <span class="na">name=</span><span class="s">"apple-mobile-web-app-status-bar-style"</span> <span class="na">content=</span><span class="s">"black-translucent"</span><span class="nt">></span>
<span class="nt"><meta</span> <span class="na">name=</span><span class="s">"apple-mobile-web-app-title"</span> <span class="na">content=</span><span class="s">"indie-catalog"</span><span class="nt">></span>
<span class="c"><!-- Homescreen icons --></span>
<span class="nt"><link</span> <span class="na">rel=</span><span class="s">"apple-touch-icon"</span> <span class="na">href=</span><span class="s">"icons/icon-48x48.png"</span><span class="nt">></span>
<span class="nt"><link</span> <span class="na">rel=</span><span class="s">"apple-touch-icon"</span> <span class="na">sizes=</span><span class="s">"96x96"</span> <span class="na">href=</span><span class="s">"icons/icon-96x96.png"</span><span class="nt">></span>
<span class="nt"><link</span> <span class="na">rel=</span><span class="s">"apple-touch-icon"</span> <span class="na">sizes=</span><span class="s">"144x144"</span> <span class="na">href=</span><span class="s">"icons/icon-144x144.png"</span><span class="nt">></span>
<span class="nt"><link</span> <span class="na">rel=</span><span class="s">"apple-touch-icon"</span> <span class="na">sizes=</span><span class="s">"192x192"</span> <span class="na">href=</span><span class="s">"icons/icon-192x192.png"</span><span class="nt">></span>
<span class="c"><!-- Tile icon for Windows 8 (144x144 + tile color) --></span>
<span class="nt"><meta</span> <span class="na">name=</span><span class="s">"msapplication-TileImage"</span> <span class="na">content=</span><span class="s">"icons/icon-144x144.png"</span><span class="nt">></span>
<span class="nt"><meta</span> <span class="na">name=</span><span class="s">"msapplication-TileColor"</span> <span class="na">content=</span><span class="s">"#fbbc05"</span><span class="nt">></span>
<span class="nt"><meta</span> <span class="na">name=</span><span class="s">"msapplication-tap-highlight"</span> <span class="na">content=</span><span class="s">"no"</span><span class="nt">></span>
</code></pre></div></div>
<p>The shitty part of this is that you have to generate your icons at 5 different sizes.
But, I told you, it’s easy 💰: once you do this, your PWA score will jump quite a bit (from <strong>45</strong> to <strong>64</strong>):</p>
<p><img alt="lighthouse score" src="/images/2017-07-26/after-manifest-and-icons.png" /></p>
<h3 id="2--bundle-with-the-polymer-cli">2. 🏃 Bundle with the Polymer CLI</h3>
<p>I use the <code class="language-plaintext highlighter-rouge">polymer cli</code> because it bundles and minifies my sources, and generates a Service Worker for free, and basically solves all of my PWA problems. To install it, run</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm install -g polymer-cli
</code></pre></div></div>
<p>In order to make it go, you need to create a <code class="language-plaintext highlighter-rouge">polymer.json</code> file. Here is my starting skeleton:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"entrypoint"</span><span class="p">:</span><span class="w"> </span><span class="s2">"index.html"</span><span class="p">,</span><span class="w">
</span><span class="nl">"fragments"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"some-element.html"</span><span class="p">,</span><span class="w">
</span><span class="s2">"maybe-another-element.html"</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"sources"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"src/**/*"</span><span class="p">,</span><span class="w">
</span><span class="s2">"images/**/*"</span><span class="p">,</span><span class="w">
</span><span class="s2">"i-dont-know-your-directory-structure"</span><span class="w">
</span><span class="s2">"bower.json"</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"extraDependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"manifest.json"</span><span class="p">,</span><span class="w">
</span><span class="s2">"bower_components/webcomponentsjs/*"</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"builds"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w"> </span><span class="nl">"preset"</span><span class="p">:</span><span class="w"> </span><span class="s2">"es5-bundled"</span><span class="w"> </span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w"> </span><span class="nl">"preset"</span><span class="p">:</span><span class="w"> </span><span class="s2">"es6-bundled"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"lint"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"rules"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"polymer-2"</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>Remove the <code class="language-plaintext highlighter-rouge">lint</code> rule if you don’t want to lint your code. Check the CLI’s
<a href="https://github.com/Polymer/polymer-cli">docs</a> or Polymer shop-app’s <a href="https://github.com/Polymer/shop/blob/master/polymer.json">polymer.json</a>
for more inspiration.
If you don’t plan on conditionally serving different bundles to different browsers
(ahem, IE11), you can also remove the <code class="language-plaintext highlighter-rouge">es5</code> preset.</p>
<p>Once you have that, run <code class="language-plaintext highlighter-rouge">polymer build</code>, and start serving out of your <code class="language-plaintext highlighter-rouge">build/es6-bundled</code>
directory. Eventually, this will be the directory you’ll actually serve out, so
do a gulp dance or something. 💃🎉🎁.</p>
<p>Polymer CLI works best if your <code class="language-plaintext highlighter-rouge">index.html</code> doesn’t have a bunch of imports in it (like <a href="https://github.com/PolymerLabs/indie-catalog/blob/beab690c1552c8bf7f247a1adbc5b9e45ed5940f/index.html#L39">this</a>). If that’s the case, rather than trying to fight the CLI, I recommend re-structuring
your app in an app-shelly way, like <a href="https://github.com/PolymerLabs/indie-catalog/blob/630b114c5244a7e80d9a33ce51317c00d4c25829/index.html#L56">this</a>. I’ve learnt not to fight the tools.</p>
<p>Anyway, at this point, our Lighthouse score is going to get a little bit worse. Even though this looks bad,
it actually makes sense: we converted our many little downloads into one giant
bundle that we have to wait for, so whatever incremental updates we had are
gone (don’t worry, we fix, we fix). And we still haven’t actually added a Service Worker:
<img alt="lighthouse score" src="/images/2017-07-26/after-bundle.png" /></p>
<p>Brief intermission: I (actually <a href="https://twitter.com/patrickhulce">Patrick Hulce</a>) accidentally unearthed a Lighthouse bug, and
significantly improved the performance score by moving a script from the head
to the body. This is prooobably an accident and will be fixed in the future,
but let’s document it for posterity anyway:
<img alt="lighthouse score" src="/images/2017-07-26/script-fix-score.png" /></p>
<h3 id="3--add-a-service-worker">3. 🤖 Add a Service Worker</h3>
<p>The <code class="language-plaintext highlighter-rouge">polymer cli</code>, bless its soul, actually generated a <code class="language-plaintext highlighter-rouge">service-worker.js</code> file for
us, we just haven’t added it anywhere, like our <code class="language-plaintext highlighter-rouge">index.html</code>:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><script></span>
<span class="k">if</span> <span class="p">(</span><span class="dl">'</span><span class="s1">serviceWorker</span><span class="dl">'</span> <span class="k">in</span> <span class="nb">navigator</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">load</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nb">navigator</span><span class="p">.</span><span class="nx">serviceWorker</span><span class="p">.</span><span class="nx">register</span><span class="p">(</span><span class="dl">'</span><span class="s1">service-worker.js</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="nt"></script></span>
</code></pre></div></div>
<p>With this change, Lighthouse is deeeeelighted:</p>
<p><img alt="lighthouse score" src="/images/2017-07-26/after-sw-score.png" /></p>
<p>The PWA score has improved a lot! It can actually go all the way to 91, but I’m
a) serving from localhost which doesn’t redirect HTTP traffic correctly, and b)
there’s a <a href="https://github.com/GoogleChrome/lighthouse/issues/2688">bug</a> that’s
screwing me out of some money dollars:
<img alt="lighthouse score" src="/images/2017-07-26/after-sw-pwa.png" /></p>
<p>The perf score has improved a lot, because Service Workers are caching machines
whos job is to help with perf, but our bundle size is still affecting our first paint.
Look at those screenshots! We wait almost 2.7s before we paint some yellow
on the screen! Surely we can do better:
<img alt="lighthouse score" src="/images/2017-07-26/after-sw-perf.png" /></p>
<h3 id="4--fix-first-paint">4. 🎨 Fix first paint</h3>
<p>In that screenshot again, we’re getting some
content back pretty fast (the white -> gray transition at 886 ms), but then we show
nothing while the main element, <code class="language-plaintext highlighter-rouge"><cat-alog></code>, is upgrading. To get around that,
I like to add fallback content in the light DOM of that main element. This works
because <code class="language-plaintext highlighter-rouge"><cat-alog></code> doesn’t have any slots, so once it upgrades, any content between
its opening and closing tags is nuked:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><style></span>
<span class="o">[</span><span class="nt">unresolved</span><span class="o">]</span> <span class="nt">p</span> <span class="p">{</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">30px</span><span class="p">;</span>
<span class="nl">padding</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt"></style></span>
<span class="nt"><x-app</span> <span class="na">unresolved</span><span class="nt">></span>
<span class="c"><!-- This content would be blown away when
x-app upgrades, because x-app has no slots --></span>
<span class="nt"><p></span>🙏 pls hold while fetching content<span class="nt"></p></span>
<span class="nt"></x-app></span>
</code></pre></div></div>
<p>Usually I try to <a href="https://github.com/PolymerLabs/indie-catalog/blob/master/index.html#L85">match</a> this fallback content to what the element paints
once it upgrades. It’s a little annoying because you can’t always share styles,
but most of the time (in my opinion) results in a better experience.</p>
<p>For extra bonus points, we can remove that <code class="language-plaintext highlighter-rouge">unresolved</code> attribute when
the element upgrades:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">ready</span><span class="p">()</span> <span class="p">{</span>
<span class="k">super</span><span class="p">.</span><span class="nx">ready</span><span class="p">();</span>
<span class="nx">Polymer</span><span class="p">.</span><span class="nx">RenderStatus</span><span class="p">.</span><span class="nx">afterNextRender</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">removeAttribute</span><span class="p">(</span><span class="dl">'</span><span class="s1">unresolved</span><span class="dl">'</span><span class="p">);</span>
<span class="cm">/* Other lazy code here */</span>
<span class="p">});</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This last change ends up putting us in the 💚green💚 on Lighthouse!</p>
<p>The performance of the app is looking pretty great, since we basically
moved first paint to that first downloaded byte:
<img alt="lighthouse score" src="/images/2017-07-26/final-perf.png" /></p>
<h2 id="-">🆗 🆒</h2>
<p>Final score on the deployed site is a satisfying <strong>A-</strong> across the board:
<img alt="lighthouse score" src="/images/2017-07-26/final-score.png" /></p>
<p>I didn’t try to win the Lighthouse jackpot, because I wanted to see how
far I would get with using <em>just</em> the Lighthouse instructions and score, without inspecting any of the performance/network tabs in the Dev Tools. My next step would probably be
to see whether lazy loading parts of my app will help, and a long and introspective
look at the Dev Tools Network tabs, to see what downloads I could delay.</p>
<p>Anyway,
I hope this helped, and that it showed that getting a good Lighthouse score is
mostly ceremony and hardly any goat sacrifices. ❤️</p>
An intro to web components with otters2017-06-06T00:00:00+00:00https://meowni.ca/posts/web-components-with-otters<style>
img.otter { max-height: 220px !important; }
iframe.otter {
height: 250px;
width: 100%;
margin: 0 auto;
border: 5px solid #E0F7FA;
border-radius: 3px;
}
iframe.otter-two {
width: 100%;
height: 320px;
border: 5px solid #E0F7FA;
border-radius: 3px;
padding: 0px 10px;
}
</style>
<p><img class="otter" alt="everyone keeps talking about web components, but huh?" src="/images/2017-06-06/1.png" /></p>
<p>I work on a library called <a href="https://www.polymer-project.org/">Polymer</a>, which helps you write web components faster and easier. This is awesome, but it’s only awesome if <strong>you</strong> (yes, YOU) know what a web component is, and know that you want to write one. So here’s a story about what these things are and teaches you how to use them without showing you 10 pages of docs and getting you to install tools and CLIs. Maybe it’s for you. Maybe it isn’t. In either case, it has otters.</p>
<ul id="markdown-toc">
<li><a href="#why-should-you-care" id="markdown-toc-why-should-you-care">Why should you care?</a></li>
<li><a href="#thinking-about-your-app" id="markdown-toc-thinking-about-your-app">Thinking about your app</a></li>
<li><a href="#where-do-web-components-live" id="markdown-toc-where-do-web-components-live">Where do web components live?</a></li>
<li><a href="#polyfills-and-you" id="markdown-toc-polyfills-and-you">Polyfills and you</a></li>
<li><a href="#1-i-want-to-use-someone-elses-web-component-in-my-app" id="markdown-toc-1-i-want-to-use-someone-elses-web-component-in-my-app">1. I want to use someone else’s web component in my app</a> <ul>
<li><a href="#1-install-the-web-component" id="markdown-toc-1-install-the-web-component">1. Install the web component</a></li>
<li><a href="#status-check" id="markdown-toc-status-check">Status check</a></li>
<li><a href="#2-import-it-in-the-app" id="markdown-toc-2-import-it-in-the-app">2. Import it in the app</a></li>
<li><a href="#3-insert-it-somewhere-in-the-app" id="markdown-toc-3-insert-it-somewhere-in-the-app">3. Insert it somewhere in the app</a></li>
<li><a href="#status-check-1" id="markdown-toc-status-check-1">Status check</a></li>
</ul>
</li>
<li><a href="#2-i-want-to-write-a-web-component-to-use-in-my-app" id="markdown-toc-2-i-want-to-write-a-web-component-to-use-in-my-app">2. I want to write a web component to use in my app</a></li>
<li><a href="#thats-all-there-is" id="markdown-toc-thats-all-there-is">That’s all there is!</a> <ul>
<li><a href="#-see-you-soon-new-web-component-friends" id="markdown-toc--see-you-soon-new-web-component-friends">👋 See you soon, new web component friends!</a></li>
</ul>
</li>
</ul>
<h2 id="why-should-you-care">Why should you care?</h2>
<p>Web components aren’t a new library or framework, they’re a new browser feature, and they let you write <em>encapsulated</em> and <em>reusable</em> <em>components</em> (more <a href="https://www.webcomponents.org/introduction">details</a>). If you’ve ever used an <code class="language-plaintext highlighter-rouge"><input></code> element, I like to think of it as the OG web component, because it’s exactly that. The thing
is that before web components came around, you had to wait on all browsers
to agree on a new element (like, a date picker). And even after they agreed
on a new element, it took them yeaaaaars to implement it. <code class="language-plaintext highlighter-rouge"><input type="date"></code>
was drafted in 2011 – today, 6 years later, not all browsers
have implemented it! With
web components, <strong>web developers</strong> get to write such elements, so that you don’t
have to wait for 10 years before all browsers agree that they should implement a date picker.
P cool, right?</p>
<ul>
<li>A <strong>component</strong> is a bunch of code that fits logically together, kinda like a unit
of functionality. This could be a simple widget like a fancy button or a date picker, or a more complex UI setup like “a responsive blog layout”</li>
<li><strong>Encapsulated</strong> means that an element’s styles and children are scoped to itself, so you can’t accidentally break what it looks like by using CSS haphazardly in your app</li>
<li><strong>Reusable</strong> means that if you have a web component, no matter how you wrote it, you should be able to use it in any other app, regardless of how it’s built (eg, a React app). This is different than, say, a React component, which you can’t just use in an Angular app without bringing all of React with you (and in most cases, it won’t even work)
<ul>
<li>Using other people’s web components is nice because it means you get to write less code, and you can use someone else’s code. Also, when I say “using web components”, I literally mean
writing something like <code class="language-plaintext highlighter-rouge"><emoji-picker></emoji-picker></code>, that just works out of the box.
Remember, a custom element is just an open source <code class="language-plaintext highlighter-rouge"><input></code> – whatever you can do
with <code class="language-plaintext highlighter-rouge"><input></code> you could do with a custom element.</li>
<li>Writing your own web component is nice because splitting your app in smaller pieces makes it more manageable. Sharing your own web component with others means that they could write less code and use yours! Sharing is caring <3</li>
</ul>
</li>
</ul>
<p><img class="otter" alt="so i can write <emoji-picker>, yay!" src="/images/2017-06-06/2.png" /></p>
<p>You write web components in ES6 JavaScript. Polymer is a JavaScript library that’s like jQuery for web components – you import it, it gives you a bunch of helper functions and saves you writing a lot of boilerplate code.</p>
<p>Also, bad habit: people (and me, I am those people) tend to use “custom element” and “web component” interchangeably. If you’re pedantic, they mean different things. Don’t be pedantic, it makes the otters sad.</p>
<p><img class="otter" alt="sad otter because it's getting well-actuallied" src="/images/2017-06-06/3.png" /></p>
<h2 id="thinking-about-your-app">Thinking about your app</h2>
<p>If you’re starting to think about using web components in your app, there’s basically 2 situations you could be in, and I tend to reason about each differently</p>
<ol>
<li>I want to use someone else’s web component in my app</li>
<li>I want to write a web component to use in my app</li>
</ol>
<p>There’s other things that you might eventually care about, such as making your application production ready, and improving its performance. They are super important topics, but if you’ve never used a web component before, they’re also not the most important topics for you <strong>right now</strong>.</p>
<p><img class="otter" alt="is this magic? nah it's prolly just undocumented code" src="/images/2017-06-06/4.png" /></p>
<h2 id="where-do-web-components-live">Where do web components live?</h2>
<p><img class="otter" alt="in otter space!" src="/images/2017-06-06/5.png" /></p>
<p>Web components tend to have dependencies on other web components, so you need a package manager to herd all them cats. Most of the web components out there use <a href="https://bower.io/"><code class="language-plaintext highlighter-rouge">bower</code></a>. Another popular one is <a href="https://www.npmjs.com/">npm</a> – you could think of <code class="language-plaintext highlighter-rouge">npm</code> as a package manager for your server code and <code class="language-plaintext highlighter-rouge">bower</code> for your client, and it wouldn’t be entirely incorrect.</p>
<p>The reason why you need a package manager and not just “download this element in a zip file” is that unless that element is really simple, it might have dependencies, and they’ll have dependencies, and that’s a thing for machines and not otters. If you really don’t want to use <code class="language-plaintext highlighter-rouge">bower</code>, then you’ll have to sort out flattening your dependency tree on your own using something like <code class="language-plaintext highlighter-rouge">webpack</code>. This is not the tutorial for you.</p>
<p>If you look up <code class="language-plaintext highlighter-rouge">bower</code> on the web you’ll hear things like “but bower is deprecated now” (which is true, but it’s also been unmaintained for like a year and it worked fine, so nothing is really new on that front) and “but why not npm” (because you can only have one version of the same web component in your app, and that’s hard if your dependency tree is not flat. You probably don’t actually care this).</p>
<p><img class="otter" alt="attenshun attenshun installation instructions" src="/images/2017-06-06/6.png" /></p>
<p>To <a href="https://bower.io/#install-bower">install</a> <code class="language-plaintext highlighter-rouge">bower</code>, a package manager, you must first install <code class="language-plaintext highlighter-rouge">npm</code>, a package manager. Take a moment for a concerned sigh, but install it anyway. Upshot: I promise this is the only tool I will ask you to install.</p>
<p>If you want to find otter web components, <a href="https://www.webcomponents.org/">WebComponents.org</a> is a universal catalog of web components (but not a package manager, because that would be cray). Go check it out.</p>
<h2 id="polyfills-and-you">Polyfills and you</h2>
<p><img class="otter" alt="anotter digression" src="/images/2017-06-06/7.png" /></p>
<p>Not all browsers implement features at the same rate, which means while you’re waiting for them to catch up, you need to care about polyfills. Fun fact: you know how when you have a hole in a wall, you have to put spackling paste in it to make the wall look like a wall again? Polyfilla is a brand of spackling, and that’s exactly what a polyfill is – it fills a hole in the browser, so that it looks even from the outside.</p>
<p>You have two choices:</p>
<ul>
<li>Ignore polyfills for now and just use Chrome to test your app, but know it’s going to be hella broken on other browsers</li>
<li>Care about polyfills, and include the <a href="https://github.com/webcomponents/webcomponentsjs">polyfill</a> in your app. Spoilers: it’s just a <code class="language-plaintext highlighter-rouge"><script src="some-path/webcomponents-lite.js"></script></code> include.</li>
</ul>
<p>in your applications’s <code class="language-plaintext highlighter-rouge"><head></code> tag
<img class="otter" alt="whadya think? we otter get to the code!" src="/images/2017-06-06/8.png" /></p>
<h2 id="1-i-want-to-use-someone-elses-web-component-in-my-app">1. I want to use someone else’s web component in my app</h2>
<p>Ok cool, so here we are. You have a web site, and you want to use someone else’s web widget in it. Let’s say that thing is <a href="https://www.webcomponents.org/element/PolymerElements/paper-button">paper-button</a> which is a super fancy looking Material Design button. It’s beauty and it’s grace, it’s Miss United States.</p>
<p>Here is a <a href="https://glitch.com/edit/#!/use-custom-element">glitch app</a> if you want to follow along at home. Glitch is an online code editor, where you can build apps and modify other people’s – which is why it’s great for this example!</p>
<p>We need to do 3 things:</p>
<h3 id="1-install-the-web-component">1. Install the web component</h3>
<ul>
<li>
<p>We do this by adding a <code class="language-plaintext highlighter-rouge">dependency</code> to our <code class="language-plaintext highlighter-rouge">bower.json</code> file. If you don’t already have a <code class="language-plaintext highlighter-rouge">bower.json</code> file (who can blame you), create one by running <code class="language-plaintext highlighter-rouge">bower init</code>, and answering the wizard’s questions. They kind of look like this, though, spoiler alert: there’s no actual wizard 😭
<img class="otter" alt="screenshot of the bower init wizard" src="/images/2017-06-06/9.png" /></p>
</li>
<li>To add <code class="language-plaintext highlighter-rouge">paper-button</code> as a dependency, you can either run
<code class="language-plaintext highlighter-rouge">bower install --save PolymerElements/paper-button#^2.0.0</code>
or by manually adding it to the <code class="language-plaintext highlighter-rouge">bower.json</code> file in its <code class="language-plaintext highlighter-rouge">dependencies</code> section:
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"dependencies": {
"paper-button": "PolymerElements/paper-button#^2.0.0"
}
</code></pre></div> </div>
</li>
<li>Because I promised you no magic: <code class="language-plaintext highlighter-rouge">^2.0.0</code> just means “the latest version between 2.0.0 to 3.0.0”. The reason I picked that version is that it’s the latest one.</li>
<li>If you manually added the element to <code class="language-plaintext highlighter-rouge">bower.json</code>, you need to actually install it, so run <code class="language-plaintext highlighter-rouge">bower install</code>.</li>
</ul>
<h3 id="status-check">Status check</h3>
<p>Right now you should have a <code class="language-plaintext highlighter-rouge">bower_components</code> folder created, that contains a whole bunch of folders, one of which is called <code class="language-plaintext highlighter-rouge">paper-button</code>.</p>
<h3 id="2-import-it-in-the-app">2. Import it in the app</h3>
<p>This basically tells the browser where to find the definition for what this <code class="language-plaintext highlighter-rouge">paper-button</code> tag is.</p>
<p>Much like how you import a CSS stylesheet with</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"/style.css"</span><span class="nt">></span>
</code></pre></div></div>
<p>You import a web component with an <strong>HTML import</strong>:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt"><link</span> <span class="na">rel=</span><span class="s">"import"</span> <span class="na">href=</span><span class="s">"/bower_components/paper-button/paper-button.html"</span><span class="nt">></span>
</code></pre></div></div>
<h3 id="3-insert-it-somewhere-in-the-app">3. Insert it somewhere in the app</h3>
<p>Drop a <code class="language-plaintext highlighter-rouge"><paper-button>Click me</paper-button></code> somewhere in your html page.</p>
<h3 id="status-check-1">Status check</h3>
<p>Your <code class="language-plaintext highlighter-rouge">index.html</code> should basically look like this:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><!DOCTYPE html></span>
<span class="nt"><html></span>
<span class="nt"><head></span>
<span class="c"><!-- Load the polyfill so that the demo works everywhere --></span>
<span class="nt"><script </span><span class="na">src=</span><span class="s">"/bower_components/webcomponentsjs/webcomponents-lite.js"</span><span class="nt">></script></span>
<span class="c"><!-- Import the custom element so that the browser knows what it means --></span>
<span class="nt"><link</span> <span class="na">rel=</span><span class="s">"import"</span> <span class="na">href=</span><span class="s">"/bower_components/paper-button/paper-button.html"</span><span class="nt">></span>
<span class="nt"></head></span>
<span class="nt"><body></span>
<span class="nt"><h1></span>Oh, hi there<span class="nt"></h1></span>
<span class="nt"><p></span>Have you seen this fancy button?<span class="nt"></p></span>
<span class="c"><!-- Use the custom element! --></span>
<span class="nt"><paper-button></span>Click me<span class="nt"></paper-button></span>
<span class="nt"></body></span>
<span class="nt"></html></span>
</code></pre></div></div>
<p>See that line about loading the <code class="language-plaintext highlighter-rouge">webcomponents-lite</code> polyfill? That’s the bit
that makes the demo work in browsers that don’t have web components yet 😎.
If you want to see what your app looks like without the polyfill, just comment
out that line and open your app in something like Firefox! It’s a good thing
to try out.</p>
<p>If we run that demo, it should look like this, plus or minus some
copy and styles that I’ve added:</p>
<iframe class="otter" src="https://use-custom-element.glitch.me/" frameborder="0"></iframe>
<p><br /></p>
<p>Now, say it together with the otters!
<img class="otter" alt="bower summary" src="/images/2017-06-06/10.png" />
<br /></p>
<p>You could, of course, add JavaScript to that custom element, like you would
with any other <code class="language-plaintext highlighter-rouge"><button></code> or <code class="language-plaintext highlighter-rouge"><input></code>. I could’ve added something like this:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">'</span><span class="s1">paper-button</span><span class="dl">'</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">click</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">alert</span><span class="p">(</span><span class="dl">'</span><span class="s1">you did a click!</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>
<h2 id="2-i-want-to-write-a-web-component-to-use-in-my-app">2. I want to write a web component to use in my app</h2>
<p>Now that we know how to import someone else’s custom element, let’s write our own! Up until now we haven’t actually talked about Polymer at all. You can totally write web components <em>without</em> Polymer; you’ll just have to write a lot more boilerplate, and I don’t want that to turn you off web components.</p>
<p>The Polymer site actually has an awesome <a href="https://www.polymer-project.org/2.0/start/first-element/intro">getting started</a> tutorial, if you’d rather read that. But while I have you here, you’ll get the otter way.</p>
<p>In Polymer, every custom element is like a taco (bear with me). There’s something called a <code class="language-plaintext highlighter-rouge">dom-module</code> (which is actually a custom element itself) that holds 2 things in it:</p>
<ol>
<li>the <code class="language-plaintext highlighter-rouge"><template></code>, or what your element looks like (html and css). A <code class="language-plaintext highlighter-rouge"><template></code> is an HTML element that’s inert – when the browser sees it, it skips it and doesn’t render it</li>
<li>a <code class="language-plaintext highlighter-rouge"><script></code>, which is what your element does</li>
</ol>
<p><strong>Sidebar</strong>: This is one of the advantages of using Polymer – without
Polymer, you’d have to construct all your HTML and CSS in JavaScript, and somehow
add it to your custom element. Polymer makes it easier for you to point at
an implementation, and at a bunch of HTML/CSS and say “that look goes with that code”.</p>
<p><br />
We add all our code to an <code class="language-plaintext highlighter-rouge">.html</code> file (because remember: we’re going to do an HTML import to bring it in our app later). It ends up looking like this:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><dom-module</span> <span class="na">id=</span><span class="s">"my-element"</span><span class="nt">></span>
<span class="nt"><template></span>
<span class="c"><!-- Any CSS your element needs for styling --></span>
<span class="nt"><style></span>
<span class="c">/* This is a special selector that styles the element itself */</span>
<span class="nd">:host</span> <span class="p">{</span> <span class="err">...</span> <span class="p">}</span>
<span class="c">/* Use the usual CSS selectors for its children */</span>
<span class="nt">p</span> <span class="p">{</span> <span class="nl">color</span><span class="p">:</span> <span class="no">tomato</span><span class="p">;</span> <span class="p">}</span>
<span class="nt"></style></span>
<span class="c"><!-- that <p> style there applies here --></span>
<span class="nt"><p></span>I should be a nice red!<span class="nt"></p></span>
<span class="nt"></template></span>
<span class="nt"><script></span>
<span class="c1">// Every custom element is an ES6 class.</span>
<span class="c1">// This is the implementation of the element.</span>
<span class="kd">class</span> <span class="nx">MyElement</span> <span class="kd">extends</span> <span class="nx">Polymer</span><span class="p">.</span><span class="nx">Element</span> <span class="p">{</span>
<span class="p">...</span>
<span class="p">}</span>
<span class="c1">// Every custom element needs to be registered.</span>
<span class="c1">// This tells the browser that the </span><span class="o"><</span><span class="nx">happy</span><span class="o">-</span><span class="nx">thing</span><span class="o">></span>
<span class="c1">// tag uses _this_ implementation.</span>
<span class="nx">customElements</span><span class="p">.</span><span class="nx">define</span><span class="p">(</span><span class="nx">MyElement</span><span class="p">.</span><span class="nx">is</span><span class="p">,</span> <span class="nx">MyElement</span><span class="p">);</span>
<span class="nt"></script></span>
<span class="nt"></dom-module></span>
</code></pre></div></div>
<p>I tend to put one element per <code class="language-plaintext highlighter-rouge">html</code> file, and then name the file after the tag of the element,
so I can keep track of it. I would save that into a <code class="language-plaintext highlighter-rouge">my-element.html</code> file, and then import it in our app, just as before with:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><link</span> <span class="na">rel=</span><span class="s">"import"</span> <span class="na">href=</span><span class="s">"my-element.html"</span><span class="nt">></span>
</code></pre></div></div>
<p><br />
Now, what goes <em>inside</em> your custom element is really up to you. The way I write elements is</p>
<ul>
<li>by either knowing ahead of time what that element should do (“I need an emoji-picker, so
it should be a text field with a button and when you click on that button, you get a list of emoji”),</li>
<li>or I am working on the app, and at some point there’s just too much HTML/CSS in the same place that looks like it can just be modularized away (“oh, all this code just deals with writing a tweet, it should probably just go into a <code class="language-plaintext highlighter-rouge"><new-tweet></code> element”)</li>
</ul>
<p>As a slightly more complicated example, I made a <code class="language-plaintext highlighter-rouge"><happy-thing></code> element in this
<a href="https://glitch.com/edit/#!/polymer-custom-element">glitch app</a> (check out the
<a href="https://glitch.com/edit/#!/polymer-custom-element?path=public/happy-thing.html:1:0">code</a>). It basically takes whatever content you give to it, and when you hover over, it does a little shimmy animation:</p>
<iframe class="otter-two" src="https://polymer-custom-element.glitch.me/" frameborder="0"></iframe>
<p><br />
I also made that element with <a href="https://glitch.com/edit/#!/simple-custom-element">plain JavaScript</a>, without Polymer, if you want to compare it. You’ll notice the second example has a lot more boilerplate code,
which Polymer abstracted out for you.</p>
<h2 id="thats-all-there-is">That’s all there is!</h2>
<p>I hope this helped! I’m going to write another post in more detail about <em>actually</em>
getting started with Polymer, and what kind of things you can do with it soon!
In the meantime, here’s some other links to get you going:</p>
<ul>
<li>Google developers <a href="https://developers.google.com/web/fundamentals/getting-started/primers/customelements">blog post</a> on
custom elements</li>
<li>The Polymer <a href="https://www.polymer-project.org/2.0/start/">getting started</a> tutorial</li>
<li>The Polymer <a href="https://www.polymer-project.org/2.0/start/quick-tour">quick tour</a> of features</li>
<li>A Polymer <a href="https://meowni.ca/posts/polymer-2-cheatsheet/">cheat sheet</a></li>
<li>The Polymer <a href="https://www.polymer-project.org/2.0/docs/devguide/feature-overview">docs</a></li>
</ul>
<p><img class="otter" alt="let me know if this was useful to you! cause i'm otter here" src="/images/2017-06-06/11.png" /></p>
<h3 id="-see-you-soon-new-web-component-friends">👋 See you soon, new web component friends!</h3>
Polymer 2.x Cheat Sheet2017-05-31T00:00:00+00:00https://meowni.ca/posts/polymer-2-cheatsheet<style>
.highlight .err {
color: inherit;
background-color: inherit;
}
.highlight .s1, .highlight .s {
color: #336699;
background: inherit;
}
pre {
margin-bottom: 30px;
}
pre, code {
background: #f4f6f8;
}
p > code, li > code {
font-weight: bold;
}
pre {
border-bottom: solid 1px #CFD8DC;
}
</style>
<p>This is a cheat sheet for the <a href="https://www.polymer-project.org/">Polymer 2.x</a> library.
If you’re looking for the Polymer 1.x cheat sheet, it is <a href="../polymer-cheatsheet/">here</a>. If you think something
is missing from this page, <a href="https://twitter.com/intent/tweet?original_referer=https%3A%2F%2Fmeowni.ca%2F&ref_src=twsrc%5Etfw&text=@notwaldorf%20Polymer%202%20cheat%20sheet%20feature%20request:">tell me</a> about it!</p>
<ul id="markdown-toc">
<li><a href="#defining-an-element" id="markdown-toc-defining-an-element">Defining an element</a></li>
<li><a href="#extending-an-element" id="markdown-toc-extending-an-element">Extending an element</a></li>
<li><a href="#defining-a-mixin" id="markdown-toc-defining-a-mixin">Defining a mixin</a></li>
<li><a href="#lifecycle-methods" id="markdown-toc-lifecycle-methods">Lifecycle methods</a></li>
<li><a href="#data-binding" id="markdown-toc-data-binding">Data binding</a></li>
<li><a href="#observers" id="markdown-toc-observers">Observers</a></li>
<li><a href="#listeners" id="markdown-toc-listeners">Listeners</a></li>
<li><a href="#properties-block" id="markdown-toc-properties-block">Properties block</a></li>
<li><a href="#observing-added-and-removed-children" id="markdown-toc-observing-added-and-removed-children">Observing added and removed children</a></li>
<li><a href="#style-modules" id="markdown-toc-style-modules">Style modules</a></li>
<li><a href="#styling-with-custom-properties-and-mixins" id="markdown-toc-styling-with-custom-properties-and-mixins">Styling with custom properties and mixins</a></li>
<li><a href="#binding-helper-elements" id="markdown-toc-binding-helper-elements">Binding helper elements</a></li>
</ul>
<h2 id="defining-an-element">Defining an element</h2>
<p>Docs: <a href="https://www.polymer-project.org/2.0/docs/upgrade">1.x -> 2.x upgrade guide</a>,
<a href="https://www.polymer-project.org/2.0/docs/devguide/registering-elements">registering an element</a>,
<a href="https://www.polymer-project.org/2.0/docs/devguide/style-shadow-dom#style-modules">shared style modules</a>.</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><link</span> <span class="na">rel=</span><span class="s">"import"</span> <span class="na">href=</span><span class="s">"bower_components/polymer/polymer-element.html"</span><span class="nt">></span>
<span class="nt"><dom-module</span> <span class="na">id=</span><span class="s">"element-name"</span><span class="nt">></span>
<span class="nt"><template></span>
<span class="c"><!-- Use one of these style declarations, but not both --></span>
<span class="c"><!-- Use this if you don’t want to include a shared style --></span>
<span class="nt"><style></style></span>
<span class="c"><!-- Use this if you want to include a shared style --></span>
<span class="nt"><style </span><span class="na">include=</span><span class="s">"some-style-module-name"</span><span class="nt">></style></span>
<span class="nt"></template></span>
<span class="nt"><script></span>
<span class="kd">class</span> <span class="nx">MyElement</span> <span class="kd">extends</span> <span class="nx">Polymer</span><span class="p">.</span><span class="nx">Element</span> <span class="p">{</span>
<span class="kd">static</span> <span class="kd">get</span> <span class="nx">is</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="dl">'</span><span class="s1">element-name</span><span class="dl">'</span><span class="p">;</span> <span class="p">}</span>
<span class="c1">// All of these are optional. Only keep the ones you need.</span>
<span class="kd">static</span> <span class="kd">get</span> <span class="nx">properties</span><span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>
<span class="kd">static</span> <span class="kd">get</span> <span class="nx">observers</span><span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>
<span class="p">}</span>
<span class="c1">// Associate the new class with an element name</span>
<span class="nx">customElements</span><span class="p">.</span><span class="nx">define</span><span class="p">(</span><span class="nx">MyElement</span><span class="p">.</span><span class="nx">is</span><span class="p">,</span> <span class="nx">MyElement</span><span class="p">);</span>
<span class="nt"></script></span>
<span class="nt"></dom-module></span>
</code></pre></div></div>
<p>To get the class definition for a particular custom tag, you can use
<code class="language-plaintext highlighter-rouge">customElements.get('element-name')</code>.</p>
<h2 id="extending-an-element">Extending an element</h2>
<p>Docs: <a href="https://www.polymer-project.org/2.0/docs/devguide/custom-elements#extending-other-elements">extending elements</a>, <a href="https://www.polymer-project.org/2.0/docs/devguide/dom-template#inherited-templates">inherited templates</a>.</p>
<p>Instead of <code class="language-plaintext highlighter-rouge">Polymer.Element</code>, a custom element can extend a different element):</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">ParentElement</span> <span class="kd">extends</span> <span class="nx">Polymer</span><span class="p">.</span><span class="nx">Element</span> <span class="p">{</span>
<span class="cm">/* ... */</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="nx">ChildElement</span> <span class="kd">extends</span> <span class="nx">ParentElement</span> <span class="p">{</span>
<span class="cm">/* ... */</span>
<span class="p">}</span>
</code></pre></div></div>
<p>To change or add to the parent’s template, override the <code class="language-plaintext highlighter-rouge">template</code> getter:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><dom-module</span> <span class="na">id=</span><span class="s">"child-element"</span><span class="nt">></span>
<span class="nt"><template></span>
<span class="nt"><style></span> <span class="c">/* ... */</span> <span class="nt"></style></span>
<span class="nt"><span></span>bonus!<span class="nt"></span></span>
<span class="nt"></template></span>
<span class="nt"><script></span>
<span class="kd">var</span> <span class="nx">childTemplate</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">childTemplate</span> <span class="o">=</span> <span class="nx">Polymer</span><span class="p">.</span><span class="nx">DomModule</span><span class="p">.</span><span class="k">import</span><span class="p">(</span><span class="dl">'</span><span class="s1">child-element</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">template</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">parentTemplate</span> <span class="o">=</span> <span class="nx">ParentElement</span><span class="p">.</span><span class="nx">template</span><span class="p">.</span><span class="nx">cloneNode</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span>
<span class="c1">// Or however you want to assemble these.</span>
<span class="nx">childTemplate</span><span class="p">.</span><span class="nx">content</span><span class="p">.</span><span class="nx">insertBefore</span><span class="p">(</span><span class="nx">parentTemplate</span><span class="p">.</span><span class="nx">firstChild</span><span class="p">,</span> <span class="nx">parentTemplate</span><span class="p">);</span>
<span class="kd">class</span> <span class="nx">ChildElement</span> <span class="kd">extends</span> <span class="nx">ParentElement</span> <span class="p">{</span>
<span class="kd">static</span> <span class="kd">get</span> <span class="nx">is</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="dl">'</span><span class="s1">child-element</span><span class="dl">'</span><span class="p">;</span> <span class="p">}</span>
<span class="c1">// Note: the more work you do here, the slower your element is to</span>
<span class="c1">// boot up. You should probably do the template assembling once, in a</span>
<span class="c1">// static method outside your class (like above).</span>
<span class="kd">static</span> <span class="kd">get</span> <span class="nx">template</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">childTemplate</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nx">customElements</span><span class="p">.</span><span class="nx">define</span><span class="p">(</span><span class="nx">ChildElement</span><span class="p">.</span><span class="nx">is</span><span class="p">,</span> <span class="nx">ChildElement</span><span class="p">);</span>
<span class="nt"></script></span>
<span class="nt"></dom-module></span>
</code></pre></div></div>
<p>If you don’t know the parent class, you can also use:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">ChildElement</span> <span class="kd">extends</span> <span class="nx">customElements</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">parent-element</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
<span class="cm">/* ... */</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="defining-a-mixin">Defining a mixin</h2>
<p>Docs: <a href="https://www.polymer-project.org/2.0/docs/devguide/custom-elements#mixins">mixins</a>,
<a href="https://www.polymer-project.org/2.0/docs/devguide/hybrid-elements">hybrid elements</a>.</p>
<p>Defining a class expression mixin to share implementation between different elements:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><script></span>
<span class="nx">MyMixin</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">superClass</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kd">class</span> <span class="kd">extends</span> <span class="nx">superClass</span> <span class="p">{</span>
<span class="c1">// Code that you want common to elements.</span>
<span class="c1">// If you're going to override a lifecycle method, remember that a) you</span>
<span class="c1">// might need to call super but b) it might not exist</span>
<span class="nx">connectedCallback</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">super</span><span class="p">.</span><span class="nx">connectedCallback</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">.</span><span class="nx">connectedCallback</span><span class="p">();</span>
<span class="p">}</span>
<span class="cm">/* ... */</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nt"></script></span>
</code></pre></div></div>
<p>Using the mixin in an element definition:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><dom-module</span> <span class="na">id=</span><span class="s">"element-name"</span><span class="nt">></span>
<span class="nt"><template></span><span class="c"><!-- ... --></span><span class="nt"></template></span>
<span class="nt"><script></span>
<span class="c1">// This could also be a sequence:</span>
<span class="c1">//class MyElement extends AnotherMixin(MyMixin(Polymer.Element)) { … }</span>
<span class="kd">class</span> <span class="nx">MyElement</span> <span class="kd">extends</span> <span class="nx">MyMixin</span><span class="p">(</span><span class="nx">Polymer</span><span class="p">.</span><span class="nx">Element</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">static</span> <span class="kd">get</span> <span class="nx">is</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="dl">'</span><span class="s1">element-name</span><span class="dl">'</span> <span class="p">}</span>
<span class="cm">/* ... */</span>
<span class="p">}</span>
<span class="nx">customElements</span><span class="p">.</span><span class="nx">define</span><span class="p">(</span><span class="nx">MyElement</span><span class="p">.</span><span class="nx">is</span><span class="p">,</span> <span class="nx">MyElement</span><span class="p">);</span>
<span class="nt"></script></span>
<span class="nt"></dom-module></span>
</code></pre></div></div>
<p>Using hybrid behaviors (defined in the 1.x syntax) as mixins:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><dom-module</span> <span class="na">id=</span><span class="s">"element-name"</span><span class="nt">></span>
<span class="nt"><template></span><span class="c"><!-- ... --></span><span class="nt"></template></span>
<span class="nt"><script></span>
<span class="kd">class</span> <span class="nx">MyElement</span> <span class="kd">extends</span> <span class="nx">Polymer</span><span class="p">.</span><span class="nx">mixinBehaviors</span><span class="p">([</span><span class="nx">MyBehavior</span><span class="p">,</span> <span class="nx">MyBehavior2</span><span class="p">],</span> <span class="nx">Polymer</span><span class="p">.</span><span class="nx">Element</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">static</span> <span class="kd">get</span> <span class="nx">is</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="dl">'</span><span class="s1">element-name</span><span class="dl">'</span> <span class="p">}</span>
<span class="cm">/* ... */</span>
<span class="p">}</span>
<span class="nx">customElements</span><span class="p">.</span><span class="nx">define</span><span class="p">(</span><span class="dl">'</span><span class="s1">element-name</span><span class="dl">'</span><span class="p">,</span> <span class="nx">MyElement</span><span class="p">);</span>
<span class="nt"></script></span>
<span class="nt"></dom-module></span>
</code></pre></div></div>
<h2 id="lifecycle-methods">Lifecycle methods</h2>
<p>Docs: <a href="https://www.polymer-project.org/2.0/docs/devguide/custom-elements#element-lifecycle">lifecycle callbacks</a>,
<a href="https://www.polymer-project.org/2.0/docs/devguide/custom-elements#one-time-initialization">ready</a>.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">MyElement</span> <span class="kd">extends</span> <span class="nx">Polymer</span><span class="p">.</span><span class="nx">Element</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">()</span> <span class="p">{</span> <span class="k">super</span><span class="p">();</span> <span class="cm">/* ... */</span><span class="p">}</span>
<span class="nx">ready</span><span class="p">()</span> <span class="p">{</span> <span class="k">super</span><span class="p">.</span><span class="nx">ready</span><span class="p">();</span> <span class="cm">/* ... */</span><span class="p">}</span>
<span class="nx">connectedCallback</span><span class="p">()</span> <span class="p">{</span> <span class="k">super</span><span class="p">.</span><span class="nx">connectedCallback</span><span class="p">();</span> <span class="cm">/* ... */</span><span class="p">}</span>
<span class="nx">disconnectedCallback</span><span class="p">()</span> <span class="p">{</span> <span class="k">super</span><span class="p">.</span><span class="nx">disconnectedCallback</span><span class="p">();</span> <span class="cm">/* ... */</span><span class="p">}</span>
<span class="nx">attributeChangedCallback</span><span class="p">()</span> <span class="p">{</span> <span class="k">super</span><span class="p">.</span><span class="nx">attributeChangedCallback</span><span class="p">();</span> <span class="cm">/* ... */</span><span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="data-binding">Data binding</h2>
<p>Docs: <a href="https://www.polymer-project.org/2.0/docs/devguide/data-binding">data binding</a>,
<a href="https://www.polymer-project.org/2.0/docs/devguide/data-binding#attribute-binding">attribute binding</a>,
<a href="https://www.polymer-project.org/2.0/docs/devguide/data-binding#bind-array-item">binding to array items</a>,
<a href="https://www.polymer-project.org/2.0/docs/devguide/data-binding#annotated-computed">computed bindings</a>.</p>
<p>Don’t forget: Polymer <a href="https://www.polymer-project.org/2.0/docs/devguide/properties#property-name-mapping">camel-cases</a> properties, so if in JavaScript you use <code class="language-plaintext highlighter-rouge">myProperty</code>,
in HTML you would use <code class="language-plaintext highlighter-rouge">my-property</code>.</p>
<p><strong>One way</strong> binding: when <code class="language-plaintext highlighter-rouge">myProperty</code> changes, <code class="language-plaintext highlighter-rouge">theirProperty</code> gets updated:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><some-element</span> <span class="na">their-property=</span><span class="s">"[[myProperty]]"</span><span class="nt">></some-element></span>
</code></pre></div></div>
<p><strong>Two way</strong> binding: when <code class="language-plaintext highlighter-rouge">myProperty</code> changes, <code class="language-plaintext highlighter-rouge">theirProperty</code> gets updated,
and vice versa:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><some-element</span> <span class="na">their-property=</span><span class="s">"{{myProperty}}"</span><span class="nt">></some-element></span>
</code></pre></div></div>
<p><strong>Attribute binding</strong>: when <code class="language-plaintext highlighter-rouge">myProperty</code> is <code class="language-plaintext highlighter-rouge">true</code>, the element is hidden; when it’s
<code class="language-plaintext highlighter-rouge">false</code>, the element is visible. The difference between attribute and property
binding is that property binding is equivalent to <code class="language-plaintext highlighter-rouge">someElement.someProp = value</code>,
whereas attribute binding is equivalent to: <code class="language-plaintext highlighter-rouge">someElement.setAttribute(someProp, value)</code></p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><some-element</span> <span class="na">hidden</span><span class="err">$="[[</span><span class="na">myProperty</span><span class="err">]]"</span><span class="nt">></some-element></span>
</code></pre></div></div>
<p><strong>Computed binding</strong>: binding to the <code class="language-plaintext highlighter-rouge">class</code> attribute will recompile styles when
<code class="language-plaintext highlighter-rouge">myProperty</code> changes:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><some-element</span> <span class="na">class</span><span class="err">$="[[</span><span class="na">_computeSomething</span><span class="err">(</span><span class="na">myProperty</span><span class="err">)]]"</span><span class="nt">></some-element></span>
<span class="nt"><script></span>
<span class="nx">_computeSomething</span><span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">prop</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">prop</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">a-class-name</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">another-class-name</span><span class="dl">'</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt"></script></span>
</code></pre></div></div>
<h2 id="observers">Observers</h2>
<p>Docs: <a href="https://www.polymer-project.org/2.0/docs/devguide/observers">observers</a>,
<a href="https://www.polymer-project.org/2.0/docs/devguide/observers#multi-property-observers">multi-property observers</a>,
<a href="https://www.polymer-project.org/2.0/docs/devguide/observers#array-observation">observing array mutations</a>,
<a href="https://www.polymer-project.org/2.0/docs/devguide/observers#dynamic-observers">adding observers dynamically</a>.</p>
<p>Adding an <code class="language-plaintext highlighter-rouge">observer</code> in the <code class="language-plaintext highlighter-rouge">properties</code> block lets you observe changes in the
value of a property:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">static</span> <span class="kd">get</span> <span class="nx">properties</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">myProperty</span><span class="p">:</span> <span class="p">{</span>
<span class="na">observer</span><span class="p">:</span> <span class="dl">'</span><span class="s1">_myPropertyChanged</span><span class="dl">'</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// The second argument is optional, and gives you the</span>
<span class="c1">// previous value of the property, before the update:</span>
<span class="nx">_myPropertyChanged</span><span class="p">(</span><span class="nx">value</span><span class="p">,</span> <span class="cm">/*oldValue */</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
</code></pre></div></div>
<p>In the <code class="language-plaintext highlighter-rouge">observers</code> block:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">static</span> <span class="kd">get</span> <span class="nx">observers</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">[</span>
<span class="dl">'</span><span class="s1">_doSomething(myProperty)</span><span class="dl">'</span><span class="p">,</span>
<span class="dl">'</span><span class="s1">_multiPropertyObserver(myProperty, anotherProperty)</span><span class="dl">'</span><span class="p">,</span>
<span class="dl">'</span><span class="s1">_observerForASubProperty(user.name)</span><span class="dl">'</span><span class="p">,</span>
<span class="c1">// Below, items can be an array or an object:'</span>
<span class="dl">'</span><span class="s1">_observerForABunchOfSubPaths(items.*)</span><span class="dl">'</span>
<span class="p">]</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Adding an observer dynamically for a property <code class="language-plaintext highlighter-rouge">otherProperty</code>:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Define a method</span>
<span class="nx">_otherPropertyChanged</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
<span class="c1">// Call it when `otherPropety` changes</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_createPropertyObserver</span><span class="p">(</span><span class="dl">'</span><span class="s1">otherProperty</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">_otherPropertyChanged</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
</code></pre></div></div>
<h2 id="listeners">Listeners</h2>
<p>In Polymer 2.0, we recommend that rather than using the <code class="language-plaintext highlighter-rouge">listeners</code> block,
you #useThePlatform and define event listeners yourself:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">ready</span><span class="p">()</span> <span class="p">{</span>
<span class="k">super</span><span class="p">.</span><span class="nx">ready</span><span class="p">();</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">some-event</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="k">this</span><span class="p">.</span><span class="nx">someFunction</span><span class="p">());</span>
<span class="p">}</span>
</code></pre></div></div>
<p>There is a <a href="https://github.com/Polymer/polymer/pull/4632">PR</a> out to add a
declarative listener block as a mixin. Stay tuned!</p>
<h2 id="properties-block">Properties block</h2>
<p>Docs: <a href="https://www.polymer-project.org/2.0/docs/devguide/properties">declared properties</a>,
<a href="https://www.polymer-project.org/2.0/docs/devguide/properties#configuring-object-and-array-properties">object/array properties</a>,
<a href="https://www.polymer-project.org/2.0/docs/devguide/properties#read-only">read-only properties</a>,
<a href="https://www.polymer-project.org/2.0/docs/devguide/observers#computed-properties">computed properties</a>,
<a href="https://www.polymer-project.org/2.0/docs/devguide/observers#add-a-computed-property-dynamically">adding computed properties dynamically</a>.</p>
<p>There are all the possible things you can use in the <code class="language-plaintext highlighter-rouge">properties</code>
block. Don’t just use all of them because you can; some (like <code class="language-plaintext highlighter-rouge">reflectToAttribute</code>
and <code class="language-plaintext highlighter-rouge">notify</code>) can have performance implications.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">static</span> <span class="kd">get</span> <span class="nx">properties</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">basic</span><span class="p">:</span> <span class="p">{</span>
<span class="na">type</span><span class="p">:</span> <span class="nb">Boolean</span> <span class="o">|</span> <span class="nb">Number</span> <span class="o">|</span> <span class="nb">String</span> <span class="o">|</span> <span class="nb">Array</span> <span class="o">|</span> <span class="nb">Object</span><span class="p">,</span>
<span class="c1">// Default value of the property can be one of the types above, eg:</span>
<span class="na">value</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="c1">// For an Array or Object, you must return it from a function</span>
<span class="c1">// (otherwise the array will be defined on the prototype</span>
<span class="c1">// and not the instance):</span>
<span class="na">value</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="p">[</span><span class="dl">'</span><span class="s1">cheese</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">pepperoni</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">more-cheese</span><span class="dl">'</span><span class="p">]</span> <span class="p">},</span>
<span class="na">reflectToAttribute</span><span class="p">:</span> <span class="kc">true</span> <span class="o">|</span> <span class="kc">false</span><span class="p">,</span>
<span class="na">readOnly</span><span class="p">:</span> <span class="kc">true</span> <span class="o">|</span> <span class="kc">false</span><span class="p">,</span>
<span class="na">notify</span><span class="p">:</span> <span class="kc">true</span> <span class="o">|</span> <span class="kc">false</span>
<span class="p">},</span>
<span class="c1">// Computed properties are essentially read-only, and can only be</span>
<span class="c1">// updated when their dependencies change.</span>
<span class="na">basicComputedProperty</span><span class="p">:</span> <span class="p">{</span>
<span class="na">computed</span><span class="p">:</span> <span class="dl">'</span><span class="s1">_someFunction(myProperty, anotherProperty)</span><span class="dl">'</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Adding a computed property dynamically:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">this</span><span class="p">.</span><span class="nx">_createComputedProperty</span><span class="p">(</span><span class="dl">'</span><span class="s1">newProperty</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">_computeNewProperty(prop1,prop2)</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
</code></pre></div></div>
<h2 id="observing-added-and-removed-children">Observing added and removed children</h2>
<p>Docs: <a href="https://www.polymer-project.org/2.0/docs/devguide/shadow-dom#shadow-dom-and-composition">Shadow DOM distribution</a>,
<a href="https://www.polymer-project.org/2.0/docs/upgrade#polymer-dom-apis">observe nodes</a>.</p>
<p>If you have a content node for distribution:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><template></span>
<span class="nt"><slot></slot></span>
<span class="nt"></template></span>
</code></pre></div></div>
<p>And you want to be notified when nodes have been added/removed:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"><!-- You need to import the observer --></span>
<span class="nt"><link</span> <span class="na">rel=</span><span class="s">"import"</span> <span class="na">href=</span><span class="s">"/bower_components/polymer/lib/utils/flattened-nodes-observer.html"</span><span class="nt">></span>
<span class="nt"><script></span>
<span class="kd">class</span> <span class="nx">MyElement</span> <span class="kd">extends</span> <span class="nx">Polymer</span><span class="p">.</span><span class="nx">Element</span> <span class="p">{</span>
<span class="cm">/* ... */</span>
<span class="nl">connectedCallback</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">super</span><span class="p">.</span><span class="nx">connectedCallback</span><span class="p">();</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_observer</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Polymer</span><span class="p">.</span><span class="nx">FlattenedNodesObserver</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">info</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// info is {addedNodes: [...], removedNodes: [...]}</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="nl">disconnectedCallback</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">super</span><span class="p">.</span><span class="nx">disconnectedCallback</span><span class="p">();</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_observer</span><span class="p">.</span><span class="nx">disconnect</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nt"></script></span>
</code></pre></div></div>
<h2 id="style-modules">Style modules</h2>
<p>Docs: <a href="https://www.polymer-project.org/2.0/docs/devguide/style-shadow-dom#style-modules">shared style modules</a>.</p>
<p>Defining styles that will be shared across different elements, in a file called
<code class="language-plaintext highlighter-rouge">my-shared-styles.html</code> (for example):</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><dom-module</span> <span class="na">id=</span><span class="s">"my-shared-styles"</span><span class="nt">></span>
<span class="nt"><template></span>
<span class="nt"><style></span>
<span class="nc">.red</span> <span class="p">{</span> <span class="nl">color</span><span class="p">:</span> <span class="no">red</span><span class="p">;</span> <span class="p">}</span>
<span class="c">/* Custom property defined in the global scope */</span>
<span class="nt">html</span> <span class="p">{</span>
<span class="py">--the-best-red</span><span class="p">:</span> <span class="m">#e91e63</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt"></style></span>
<span class="nt"></template></span>
<span class="nt"></dom-module></span>
</code></pre></div></div>
<p>Include the shared style in a custom element:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><link</span> <span class="na">rel=</span><span class="s">"import"</span> <span class="na">href=</span><span class="s">"my-shared-styles.html"</span><span class="nt">></span>
<span class="nt"><dom-module</span> <span class="na">id=</span><span class="s">"element-name"</span><span class="nt">></span>
<span class="nt"><template></span>
<span class="nt"><style </span><span class="na">include=</span><span class="s">"my-shared-styles"</span><span class="nt">></span>
<span class="c">/* Other styles in here */</span>
<span class="nt"></style></span>
<span class="nt"></template></span>
<span class="nt"><script></span>
<span class="kd">class</span> <span class="nx">MyElement</span> <span class="kd">extends</span> <span class="nx">Polymer</span><span class="p">.</span><span class="nx">Element</span> <span class="p">{</span>
<span class="cm">/* ... */</span>
<span class="p">}</span>
<span class="nt"></script></span>
<span class="nt"></dom-module></span>
</code></pre></div></div>
<p>Include the shared style in the main document:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><html></span>
<span class="nt"><head></span>
<span class="c"><!-- Import the custom-style element --></span>
<span class="nt"><link</span> <span class="na">rel=</span><span class="s">"import"</span> <span class="na">href=</span><span class="s">"components/polymer/lib/elements/custom-style.html"</span><span class="nt">></span>
<span class="nt"><link</span> <span class="na">rel=</span><span class="s">"import"</span> <span class="na">href=</span><span class="s">"my-shared-styles.html"</span><span class="nt">></span>
<span class="nt"><custom-style></span>
<span class="nt"><style </span><span class="na">include=</span><span class="s">"my-shared-styles"</span><span class="nt">></span>
<span class="c">/* Other styles in here */</span>
<span class="nt"></style></span>
<span class="nt"></custom-style></span>
<span class="nt"></head></span>
<span class="nt"><body></span>...<span class="nt"></body></span>
<span class="nt"></html></span>
</code></pre></div></div>
<h2 id="styling-with-custom-properties-and-mixins">Styling with custom properties and mixins</h2>
<p>Docs: <a href="https://www.polymer-project.org/2.0/docs/devguide/style-shadow-dom">styling</a>,
<a href="https://www.polymer-project.org/2.0/docs/devguide/custom-css-properties">CSS properties</a>,
<a href="https://www.polymer-project.org/2.0/docs/devguide/custom-css-properties#use-custom-css-mixins">CSS mixins</a>,
<a href="https://www.polymer-project.org/2.0/docs/devguide/custom-css-properties#custom-properties-shim-limitations">shim limitations</a></p>
<p>Note that the examples below depend on browser support for custom properties and mixins.</p>
<p>Defining a custom property:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">html</span> <span class="c">/* or :host etc. */</span><span class="p">{</span>
<span class="py">--my-custom-radius</span><span class="p">:</span> <span class="m">5px</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Using a custom property:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.my-image</span> <span class="p">{</span>
<span class="nl">border-radius</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--my-custom-radius</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Using a custom property with a fallback:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.my-image</span> <span class="p">{</span>
<span class="nl">border-radius</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--my-custom-radius</span><span class="p">,</span> <span class="m">3px</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Using a custom property with a custom property fallback:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.my-image</span> <span class="p">{</span>
<span class="nl">border-radius</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--my-custom-radius</span><span class="p">,</span> <span class="n">var</span><span class="p">(</span><span class="n">--my-fallback</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div></div>
<p>If you want to use mixins, you need to include the CSS mixins shim.
For how to use the shim and its limitations, check the docs linked at the
beginning of the section.</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><link</span> <span class="na">rel=</span><span class="s">"import"</span> <span class="na">href=</span><span class="s">"/bower_components/shadycss/apply-shim.html"</span><span class="nt">></span>
</code></pre></div></div>
<p>Defining a mixin:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">some-custom-element</span> <span class="p">{</span>
<span class="py">--my-custom-mixin</span><span class="p">:</span> <span class="err">{</span>
<span class="n">border-radius</span><span class="p">:</span> <span class="m">5px</span><span class="p">;</span>
<span class="p">}</span><span class="o">;</span>
<span class="err">}</span>
</code></pre></div></div>
<p>Using a mixin:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.my-image</span> <span class="p">{</span>
<span class="err">@apply</span> <span class="err">--my-custom-mixin;</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="binding-helper-elements">Binding helper elements</h2>
<p>Docs: <a href="https://www.polymer-project.org/2.0/docs/devguide/templates#dom-repeat">dom-repeat</a>,
<a href="https://www.polymer-project.org/2.0/docs/devguide/templates#dom-bind">dom-bind</a>,
<a href="https://www.polymer-project.org/2.0/docs/devguide/templates#dom-if">dom-if</a></p>
<p>There are two ways to use the helper elements:</p>
<ul>
<li>inside a Polymer element/Polymer managed template: just use the <code class="language-plaintext highlighter-rouge"><template is=...></code>
syntax, without the wrapper, for example:</li>
</ul>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><template</span> <span class="na">is=</span><span class="s">"dom-repeat"</span><span class="nt">></span>
...
<span class="nt"></template></span>
</code></pre></div></div>
<ul>
<li>outside of a Polymer managed template: use the <code class="language-plaintext highlighter-rouge"><dom-...></code> wrapper element
around a <code class="language-plaintext highlighter-rouge"><template></code>, for example:</li>
</ul>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><dom-repeat></span>
<span class="nt"><template></span>
...
<span class="nt"></template></span>
<span class="nt"></dom-repeat></span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">dom-repeat</code> stamps and binds a template for each item in an array:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><link</span> <span class="na">rel=</span><span class="s">"import"</span> <span class="na">href=</span><span class="s">"components/polymer/lib/elements/dom-repeat.html"</span><span class="nt">></span>
<span class="nt"><dom-repeat</span> <span class="na">items=</span><span class="s">"[[employees]]"</span><span class="nt">></span>
<span class="nt"><template></span>
<span class="nt"><div></span>First name: <span class="nt"><span></span>[[item.first]]<span class="nt"></span></div></span>
<span class="nt"><div></span>Last name: <span class="nt"><span></span>[[item.last]]<span class="nt"></span></div></span>
<span class="nt"></template></span>
<span class="nt"></dom-repeat></span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">dom-bind</code> stamps itself into the main document and adds a binding scope:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><link</span> <span class="na">rel=</span><span class="s">"import"</span> <span class="na">href=</span><span class="s">"components/polymer/lib/elements/dom-bind.html"</span><span class="nt">></span>
<span class="nt"><html></span>
<span class="nt"><body></span>
<span class="nt"><dom-bind></span>
<span class="nt"><template></span>
<span class="nt"><paper-input</span> <span class="na">value=</span><span class="s">"{{myText}}"</span><span class="nt">></paper-input></span>
<span class="nt"><span></span>You typed: [[myText]]<span class="nt"></span></span>
<span class="nt"></template></span>
<span class="nt"></dom-bind></span>
<span class="nt"></body></span>
<span class="nt"><html></span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">dom-if</code> stamps itself conditionally based on a property’s value:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><link</span> <span class="na">rel=</span><span class="s">"import"</span> <span class="na">href=</span><span class="s">"components/polymer/lib/elements/dom-if.html"</span><span class="nt">></span>
<span class="nt"><dom-if</span> <span class="na">if=</span><span class="s">"[[myProperty]]"</span><span class="nt">></span>
<span class="nt"><template></span>
<span class="nt"><span></span>This content will appear when myProperty is truthy.<span class="nt"></span></span>
<span class="nt"></template></span>
<span class="nt"></dom-if></span>
</code></pre></div></div>
2016: another year in review2016-12-21T00:00:00+00:00https://meowni.ca/posts/2016-in-review<p>This year was pretty shit, so I wasn’t very keen on doing
one of these posts. So many of my favourite musicians died, so many of my
favourite countries made mistakes, so many of my favourite people are worried and
sad, and I am worried and sad with them. But this year was not the worst year
we’ve ever had, or might ever have, so I will tell
you the good parts of my year. Maybe you’ll tell me the good parts of yours,
and then for 3 minutes we will be less worried and sad, together. ❤️</p>
<p>Here’s life as GitHub saw it. Red text is dumb projects I’ve shipped (you can
tell because the names don’t make sense), black text is
conferences I’ve spoken at:</p>
<p><img alt="2016 contribution graph with project and conferences markers" src="/images/2016-review/github.png" /></p>
<p>When I first looked at this graph I freaked out,
because it looks like I did very little until April. Then someone reminded me
that I have a job that isn’t writing emoji apps, so I probably
just you know, went to work and had normal evening hobbies like playing The Sims
and hanging out with my cat. It just takes 4 months before I get antsy.</p>
<p>Here’s life as Instagram saw it. I’ve apparently started taking a lot more
selfies, which is either pretty vain or slightly more self confident, or let’s be
honest, neither. This is also the first year when I took a lot of photos with my nice camera
when traveling, but didn’t have the energy to process or post any of them. Shit
happens.</p>
<p><img alt="2016 contribution graph" src="/images/2016-review/instagram.png" /></p>
<h2>🛫✨🛬</h2>
<p>I love traveling almost as much as I love cheese (which is a lot),
and I’m so happy about all the places I got to go to this
year. I travelled every month that didn’t start with an A. I went to 17 cities
and 8 countries.
In July, I was at home for a total of 9 days. I am lucky that I work on a team
that still tolerates the fact that every couple of months I’ll just be in a
different timezone, and I’m worried about when this will end and I’ll have to stand still.
My favourite trip
was going to Taiwan, which was a country I didn’t expect to love as
much as I did.</p>
<h2 id="️">✌️🐼</h2>
<p>I drank over 500 cups of coffee. I learnt about 60 traditional Chinese characters.
I’m not trying to learn how to speak the language, I’m just fascinated by the radicals and the writing and
the calligraphy and how some words are like a story. I might write a blog post
about it one day when I’m less nervous about it. I drew a <a href="https://dribbble.com/shots/3168680-Polymerosaurus">dinosaur</a>. I still love my job.
I still didn’t work on weekends. I still didn’t spoil Star Wars for anybody. I turned 31.</p>
<h3 id="️-1">❤️</h3>
<p>I hope your year was ok, Internet friends.</p>
<p>(you can read the <a href="../a-year-in-review">2015</a> year in review if
you’re all warm and cozy and don’t want to leave)</p>
Polymer 1.x Cheat Sheet2016-12-13T00:00:00+00:00https://meowni.ca/posts/polymer-cheatsheet<style>
.highlight .err {
color: inherit;
background-color: inherit;
}
.highlight .s1, .highlight .s {
color: #336699;
background: inherit;
}
pre {
margin-bottom: 30px;
}
pre, code {
background: #f4f6f8;
}
p > code, li > code {
font-weight: bold;
}
pre {
border-bottom: solid 1px #CFD8DC;
}
</style>
<p>This is a cheat sheet for the <a href="https://www.polymer-project.org/1.0/">Polymer 1.x</a> library.
It helps you write Web Components, which are pretty 🔥🔥🔥. If you’re interested in the
Polymer 2.0 cheat sheet, it’s <a href="https://meowni.ca/posts/polymer-2-cheatsheet/">here</a>. If you think something
is missing from this page, <a href="https://twitter.com/intent/tweet?original_referer=https%3A%2F%2Fmeowni.ca%2F&ref_src=twsrc%5Etfw&text=@notwaldorf%20Polymer%20cheat%20sheet%20feature%20request:">tell me</a> about it!</p>
<ul id="markdown-toc">
<li><a href="#defining-an-element" id="markdown-toc-defining-an-element">Defining an element</a></li>
<li><a href="#defining-a-behaviour" id="markdown-toc-defining-a-behaviour">Defining a behaviour</a></li>
<li><a href="#lifecycle-methods" id="markdown-toc-lifecycle-methods">Lifecycle methods</a></li>
<li><a href="#data-binding" id="markdown-toc-data-binding">Data binding</a></li>
<li><a href="#observers" id="markdown-toc-observers">Observers</a></li>
<li><a href="#listeners" id="markdown-toc-listeners">Listeners</a></li>
<li><a href="#properties-block" id="markdown-toc-properties-block">Properties block</a></li>
<li><a href="#observing-added-and-removed-children" id="markdown-toc-observing-added-and-removed-children">Observing added and removed children</a></li>
<li><a href="#style-modules" id="markdown-toc-style-modules">Style modules</a></li>
<li><a href="#styling-with-custom-properties-and-mixins" id="markdown-toc-styling-with-custom-properties-and-mixins">Styling with custom properties and mixins</a></li>
<li><a href="#binding-helper-elements" id="markdown-toc-binding-helper-elements">Binding helper elements</a></li>
</ul>
<h2 id="defining-an-element">Defining an element</h2>
<p>Docs: <a href="https://www.polymer-project.org/1.0/docs/devguide/registering-elements">registering an element</a>, <a href="https://www.polymer-project.org/1.0/docs/devguide/registering-elements#prototype-mixins">behaviours</a>, <a href="https://www.polymer-project.org/1.0/docs/devguide/styling#style-modules">shared style modules</a></p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><dom-module</span> <span class="na">id=</span><span class="s">"element-name"</span><span class="nt">></span>
<span class="nt"><template></span>
<span class="c"><!-- Use one of these style declarations, but not both --></span>
<span class="c"><!-- Use this if you don’t want to include a shared style --></span>
<span class="nt"><style></style></span>
<span class="c"><!-- Use this if you want to include a shared style --></span>
<span class="nt"><style </span><span class="na">include=</span><span class="s">"some-style-module-name"</span><span class="nt">></style></span>
<span class="nt"></template></span>
<span class="nt"><script></span>
<span class="nx">Polymer</span><span class="p">({</span>
<span class="na">is</span><span class="p">:</span> <span class="dl">'</span><span class="s1">element-name</span><span class="dl">'</span><span class="p">,</span>
<span class="c1">// All of these are optional. Only keep the ones you need.</span>
<span class="na">behaviors</span><span class="p">:</span> <span class="p">[],</span>
<span class="na">observers</span><span class="p">:</span> <span class="p">[],</span>
<span class="na">listeners</span><span class="p">:</span> <span class="p">{},</span>
<span class="na">hostAttributes</span><span class="p">:</span> <span class="p">{},</span>
<span class="na">properties</span><span class="p">:</span> <span class="p">{}</span>
<span class="p">});</span>
<span class="nt"></script></span>
<span class="nt"></dom-module></span>
</code></pre></div></div>
<h2 id="defining-a-behaviour">Defining a behaviour</h2>
<p>Docs: <a href="https://www.polymer-project.org/1.0/docs/devguide/behaviors">behaviours</a>.</p>
<p>Defining a behavior to share implementation between different elements:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><script></span>
<span class="nx">MyNamespace</span><span class="p">.</span><span class="nx">MyFancyBehaviorImpl</span> <span class="o">=</span> <span class="p">{</span>
<span class="c1">// Code that you want common to elements, such</span>
<span class="c1">// as behaviours, methods, etc.</span>
<span class="p">}</span>
<span class="nx">MyNamespace</span><span class="p">.</span><span class="nx">MyFancyBehavior</span> <span class="o">=</span> <span class="p">[</span>
<span class="nx">MyFancyBehaviorImpl</span><span class="p">,</span>
<span class="cm">/* You can add other behaviours here */</span>
<span class="p">];</span>
<span class="nt"></script></span>
</code></pre></div></div>
<p>Using the behavior in an element:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><dom-module</span> <span class="na">id=</span><span class="s">"element-name"</span><span class="nt">></span>
<span class="nt"><template></span>
<span class="c"><!-- ... --></span>
<span class="nt"></template></span>
<span class="nt"><script></span>
<span class="nx">Polymer</span><span class="p">({</span>
<span class="na">is</span><span class="p">:</span> <span class="dl">'</span><span class="s1">element-name</span><span class="dl">'</span><span class="p">,</span>
<span class="na">behaviors</span><span class="p">:</span> <span class="p">[</span><span class="nx">MyNamespace</span><span class="p">.</span><span class="nx">MyCustomButtonBehavior</span><span class="p">]</span>
<span class="cm">/* ... */</span>
<span class="p">});</span>
<span class="nt"></script></span>
<span class="nt"></dom-module></span>
</code></pre></div></div>
<h2 id="lifecycle-methods">Lifecycle methods</h2>
<p>Docs: <a href="https://www.polymer-project.org/1.0/docs/devguide/registering-elements#lifecycle-callbacks">lifecycle callbacks</a>.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">Polymer</span><span class="p">({</span>
<span class="na">registered</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{},</span>
<span class="na">created</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{},</span>
<span class="na">ready</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{},</span>
<span class="na">attached</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{},</span>
<span class="na">detached</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{}</span>
<span class="p">});</span>
</code></pre></div></div>
<p>There’s an <code class="language-plaintext highlighter-rouge">attributeChanged</code> callback as well, but that’s very rarely used.</p>
<h2 id="data-binding">Data binding</h2>
<p>Docs: <a href="https://www.polymer-project.org/1.0/docs/devguide/data-binding">data binding</a>,
<a href="https://www.polymer-project.org/1.0/docs/devguide/data-binding#attribute-binding">attribute binding</a>,
<a href="https://www.polymer-project.org/1.0/docs/devguide/data-binding#bind-array-item">binding to array items</a>,
<a href="https://www.polymer-project.org/1.0/docs/devguide/data-binding#annotated-computed">computed bindings</a>.</p>
<p>Don’t forget: Polymer <a href="https://www.polymer-project.org/1.0/docs/devguide/properties#property-name-mapping">camel-cases</a> properties, so if in JavaScript you use <code class="language-plaintext highlighter-rouge">myProperty</code>,
in HTML you would use <code class="language-plaintext highlighter-rouge">my-property</code>.</p>
<p><strong>One way</strong> binding: when <code class="language-plaintext highlighter-rouge">myProperty</code> changes, <code class="language-plaintext highlighter-rouge">theirProperty</code> gets updated:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><some-element</span> <span class="na">their-property=</span><span class="s">"[[myProperty]]"</span><span class="nt">></some-element></span>
</code></pre></div></div>
<p><strong>Two way</strong> binding: when <code class="language-plaintext highlighter-rouge">myProperty</code> changes, <code class="language-plaintext highlighter-rouge">theirProperty</code> gets updated,
and vice versa:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><some-element</span> <span class="na">their-property=</span><span class="s">"{{myProperty}}"</span><span class="nt">></some-element></span>
</code></pre></div></div>
<p><strong>Attribute binding</strong>: when <code class="language-plaintext highlighter-rouge">myProperty</code> is <code class="language-plaintext highlighter-rouge">true</code>, the element is hidden; when it’s
<code class="language-plaintext highlighter-rouge">false</code>, the element is visible:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><some-element</span> <span class="na">hidden</span><span class="err">$="[[</span><span class="na">myProperty</span><span class="err">]]"</span><span class="nt">></some-element></span>
</code></pre></div></div>
<p><strong>Computed binding</strong>: binding to the <code class="language-plaintext highlighter-rouge">class</code> attribute will recompile styles when
<code class="language-plaintext highlighter-rouge">myProperty</code> changes:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><some-element</span> <span class="na">class</span><span class="err">$="[[</span><span class="na">_computeSomething</span><span class="err">(</span><span class="na">myProperty</span><span class="err">)]]"</span><span class="nt">></some-element></span>
_computeSomething: function(prop) {
return prop ? 'a-class-name' : 'another-class-name';
}
</code></pre></div></div>
<h2 id="observers">Observers</h2>
<p>Docs: <a href="https://www.polymer-project.org/1.0/docs/devguide/observers">observers</a>,
<a href="https://www.polymer-project.org/1.0/docs/devguide/observers#multi-property-observers">multi-property observers</a>,
<a href="https://www.polymer-project.org/1.0/docs/devguide/observers#array-observation">observing array mutations</a>.</p>
<p>Adding an <code class="language-plaintext highlighter-rouge">observer</code> in the <code class="language-plaintext highlighter-rouge">properties</code> block lets you observe changes in the
value of a property:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">properties</span><span class="p">:</span> <span class="p">{</span>
<span class="nl">myProperty</span><span class="p">:</span> <span class="p">{</span>
<span class="na">observer</span><span class="p">:</span> <span class="dl">'</span><span class="s1">_myPropertyChanged</span><span class="dl">'</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="c1">// The second argument is optional, and gives you the</span>
<span class="c1">// previous value of the property, before the update:</span>
<span class="nx">_myPropertyChanged</span><span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">value</span><span class="p">,</span> <span class="cm">/*oldValue */</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">//...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>In the <code class="language-plaintext highlighter-rouge">observers</code> block:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">observers</span><span class="p">:</span> <span class="p">[</span>
<span class="dl">'</span><span class="s1">_doSomething(myProperty)</span><span class="dl">'</span><span class="p">,</span>
<span class="dl">'</span><span class="s1">_multiPropertyObserver(myProperty, anotherProperty)</span><span class="dl">'</span><span class="p">,</span>
<span class="dl">'</span><span class="s1">_observerForASubProperty(user.name)</span><span class="dl">'</span><span class="p">,</span>
<span class="c1">// Below, items can be an array or an object:'</span>
<span class="dl">'</span><span class="s1">_observerForABunchOfSubPaths(items.*)</span><span class="dl">'</span>
<span class="p">]</span>
</code></pre></div></div>
<h2 id="listeners">Listeners</h2>
<p>Docs: <a href="https://www.polymer-project.org/1.0/docs/devguide/events#event-listeners">event listeners</a>,
<a href="https://www.polymer-project.org/1.0/docs/devguide/events#imperative-listeners">imperative listeners</a>.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">listeners</span><span class="p">:</span> <span class="p">{</span>
<span class="dl">'</span><span class="s1">click</span><span class="dl">'</span><span class="p">:</span> <span class="dl">'</span><span class="s1">_onClick</span><span class="dl">'</span><span class="p">,</span>
<span class="dl">'</span><span class="s1">input</span><span class="dl">'</span><span class="p">:</span> <span class="dl">'</span><span class="s1">_onInput</span><span class="dl">'</span><span class="p">,</span>
<span class="dl">'</span><span class="s1">something-changed</span><span class="dl">'</span><span class="p">:</span> <span class="dl">'</span><span class="s1">_onSomethingChanged</span><span class="dl">'</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="properties-block">Properties block</h2>
<p>Docs: <a href="https://www.polymer-project.org/1.0/docs/devguide/properties">declared properties</a>,
<a href="https://www.polymer-project.org/1.0/docs/devguide/properties#configuring-object-and-array-properties">object/array properties</a>,
<a href="https://www.polymer-project.org/1.0/docs/devguide/properties#read-only">read-only properties</a>,
<a href="https://www.polymer-project.org/1.0/docs/devguide/observers#define-a-computed-property">computed properties</a>.</p>
<p>There are all the possible things you can use in the <code class="language-plaintext highlighter-rouge">properties</code>
block. Don’t just use all of them because you can; some (like <code class="language-plaintext highlighter-rouge">reflectToAttribute</code>
and <code class="language-plaintext highlighter-rouge">notify</code>) can have performance implications.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">properties</span><span class="p">:</span> <span class="p">{</span>
<span class="nl">basic</span><span class="p">:</span> <span class="p">{</span>
<span class="na">type</span><span class="p">:</span> <span class="nb">Boolean</span> <span class="o">|</span> <span class="nb">Number</span> <span class="o">|</span> <span class="nb">String</span> <span class="o">|</span> <span class="nb">Array</span> <span class="o">|</span> <span class="nb">Object</span><span class="p">,</span>
<span class="c1">// Value can be one of the types above, eg:</span>
<span class="na">value</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="c1">// For an Array or Object, you must return it from a function</span>
<span class="c1">// (otherwise the array will be defined on the prototype</span>
<span class="c1">// and not the instance):</span>
<span class="na">value</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="p">[</span><span class="dl">'</span><span class="s1">cheese</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">pepperoni</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">more-cheese</span><span class="dl">'</span><span class="p">]</span> <span class="p">},</span>
<span class="na">reflectToAttribute</span><span class="p">:</span> <span class="kc">true</span> <span class="o">|</span> <span class="kc">false</span><span class="p">,</span>
<span class="na">readOnly</span><span class="p">:</span> <span class="kc">true</span> <span class="o">|</span> <span class="kc">false</span><span class="p">,</span>
<span class="na">notify</span><span class="p">:</span> <span class="kc">true</span> <span class="o">|</span> <span class="kc">false</span>
<span class="p">},</span>
<span class="c1">// Computed properties are essentially read-only, and can only be</span>
<span class="c1">// updated when their dependencies change.</span>
<span class="nx">basicComputedProperty</span><span class="p">:</span> <span class="p">{</span>
<span class="nl">computed</span><span class="p">:</span> <span class="dl">'</span><span class="s1">_someFunction(myProperty, anotherProperty)</span><span class="dl">'</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="observing-added-and-removed-children">Observing added and removed children</h2>
<p>Docs: <a href="https://www.polymer-project.org/1.0/docs/devguide/local-dom#dom-distribution">DOM distribution</a>,
<a href="https://www.polymer-project.org/1.0/docs/devguide/local-dom#observe-nodes">observe nodes</a>.</p>
<p>If you have a content node for distribution:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><template></span>
<span class="nt"><slot</span> <span class="na">id=</span><span class="s">"distributed"</span><span class="nt">></slot></span>
<span class="nt"></template></span>
</code></pre></div></div>
<p>And you want to be notified when nodes have been added/removed:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">attached</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_observer</span> <span class="o">=</span>
<span class="nx">Polymer</span><span class="p">.</span><span class="nx">dom</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">$</span><span class="p">.</span><span class="nx">distributed</span><span class="p">).</span><span class="nx">observeNodes</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">info</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// info is {addedNodes: [...], removedNodes: [...]}</span>
<span class="p">});</span>
<span class="p">},</span>
<span class="nx">detached</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">Polymer</span><span class="p">.</span><span class="nx">dom</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">$</span><span class="p">.</span><span class="nx">distributed</span><span class="p">).</span><span class="nx">unobserveNodes</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">_observer</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="style-modules">Style modules</h2>
<p>Docs: <a href="https://www.polymer-project.org/1.0/docs/devguide/styling#style-modules">shared style modules</a>.</p>
<p>Defining styles that will be shared across different elements, in a file called
<code class="language-plaintext highlighter-rouge">my-shared-styles.html</code> (for example):</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><dom-module</span> <span class="na">id=</span><span class="s">"my-shared-styles"</span><span class="nt">></span>
<span class="nt"><template></span>
<span class="nt"><style></span>
<span class="nc">.red</span> <span class="p">{</span> <span class="nl">color</span><span class="p">:</span> <span class="no">red</span><span class="p">;</span> <span class="p">}</span>
<span class="c">/* Custom property defined in the global scope */</span>
<span class="nt">html</span> <span class="p">{</span>
<span class="py">--the-best-red</span><span class="p">:</span> <span class="m">#e91e63</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt"></style></span>
<span class="nt"></template></span>
<span class="nt"></dom-module></span>
</code></pre></div></div>
<p>Include the shared style in a custom element:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><link</span> <span class="na">rel=</span><span class="s">"import"</span> <span class="na">href=</span><span class="s">"my-shared-styles.html"</span><span class="nt">></span>
<span class="nt"><dom-module</span> <span class="na">id=</span><span class="s">"element-name"</span><span class="nt">></span>
<span class="nt"><template></span>
<span class="nt"><style </span><span class="na">include=</span><span class="s">"my-shared-styles"</span><span class="nt">></span>
<span class="c">/* Other styles in here */</span>
<span class="nt"></style></span>
<span class="nt"></template></span>
<span class="nt"><script></span>
<span class="nx">Polymer</span><span class="p">({</span> <span class="na">is</span><span class="p">:</span> <span class="dl">'</span><span class="s1">element-name</span><span class="dl">'</span> <span class="p">});</span>
<span class="nt"></script></span>
<span class="nt"></dom-module></span>
</code></pre></div></div>
<p>Include the shared style in the main document:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><html></span>
<span class="nt"><head></span>
<span class="nt"><link</span> <span class="na">rel=</span><span class="s">"import"</span> <span class="na">href=</span><span class="s">"my-shared-styles.html"</span><span class="nt">></span>
<span class="nt"><style </span><span class="na">is=</span><span class="s">"custom-style"</span> <span class="na">include=</span><span class="s">"my-shared-styles"</span><span class="nt">></span>
<span class="c">/* Other styles in here */</span>
<span class="nt"></style></span>
<span class="nt"></head></span>
<span class="nt"><body></span>...<span class="nt"></body></span>
<span class="nt"></html></span>
</code></pre></div></div>
<h2 id="styling-with-custom-properties-and-mixins">Styling with custom properties and mixins</h2>
<p>Docs: <a href="https://www.polymer-project.org/1.0/docs/devguide/styling">styling</a>,
<a href="https://www.polymer-project.org/1.0/docs/devguide/styling#custom-css-properties">CSS properties</a>,
<a href="https://www.polymer-project.org/1.0/docs/devguide/styling#custom-css-mixins">CSS mixins</a>,
<a href="https://www.polymer-project.org/1.0/docs/devguide/styling#custom-properties-shim-limitations">shim limitations</a></p>
<p>Note that the examples below depend on browser support for custom properties.
For how to use the shim (spoilers: it’s <code class="language-plaintext highlighter-rouge"><style is="custom-style"></code>) and its limitations,
check the docs linked above.</p>
<p>Defining a custom property:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">html</span> <span class="c">/* or :host, or :root etc. */</span><span class="p">{</span>
<span class="py">--my-custom-radius</span><span class="p">:</span> <span class="m">5px</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Using a custom property:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.my-image</span> <span class="p">{</span>
<span class="nl">border-radius</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--my-custom-radius</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Using a custom property with a fallback:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.my-image</span> <span class="p">{</span>
<span class="nl">border-radius</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--my-custom-radius</span><span class="p">,</span> <span class="m">3px</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Using a custom property with a custom property fallback:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.my-image</span> <span class="p">{</span>
<span class="nl">border-radius</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--my-custom-radius</span><span class="p">,</span> <span class="n">var</span><span class="p">(</span><span class="n">--my-fallback</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Defining a mixin:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">some-custom-element</span> <span class="p">{</span>
<span class="py">--my-custom-mixin</span><span class="p">:</span> <span class="err">{</span>
<span class="n">border-radius</span><span class="p">:</span> <span class="m">5px</span><span class="p">;</span>
<span class="p">}</span><span class="o">;</span>
<span class="err">}</span>
</code></pre></div></div>
<p>Using a mixin:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.my-image</span> <span class="p">{</span>
<span class="err">@apply</span> <span class="err">--my-custom-mixin;</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="binding-helper-elements">Binding helper elements</h2>
<p>Docs: <a href="https://www.polymer-project.org/1.0/docs/api/dom-repeat">dom-repeat</a>,
<a href="https://www.polymer-project.org/1.0/docs/api/dom-bind">dom-bind</a>,
<a href="https://www.polymer-project.org/1.0/docs/api/dom-if">dom-if</a></p>
<p><code class="language-plaintext highlighter-rouge">dom-repeat</code> stamps and binds a template for each item in an array:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><template</span> <span class="na">is=</span><span class="s">"dom-repeat"</span> <span class="na">items=</span><span class="s">"{{employees}}"</span><span class="nt">></span>
<span class="nt"><div></span>First name: <span class="nt"><span></span>{{item.first}}<span class="nt"></span></div></span>
<span class="nt"><div></span>Last name: <span class="nt"><span></span>{{item.last}}<span class="nt"></span></div></span>
<span class="nt"></template></span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">dom-bind</code> stamps itself into the main document and adds a binding scope:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><html></span>
<span class="nt"><body></span>
<span class="nt"><template</span> <span class="na">is=</span><span class="s">"dom-bind"</span><span class="nt">></span>
<span class="nt"><paper-input</span> <span class="na">value=</span><span class="s">"{{myText}}"</span><span class="nt">></paper-input></span>
<span class="nt"><span></span>You typed: [[myText]]<span class="nt"></span></span>
<span class="nt"></template></span>
<span class="nt"></body></span>
<span class="nt"><html></span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">dom-if</code> stamps itself conditionally based on a property’s value:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><template</span> <span class="na">is=</span><span class="s">"dom-if"</span> <span class="na">if=</span><span class="s">"{{myProperty}}"</span><span class="nt">></span>
<span class="nt"><span></span>This content will appear when myProperty is truthy.<span class="nt"></span></span>
<span class="nt"></template></span>
</code></pre></div></div>
Web fonts, boy, I don't know2016-11-01T00:00:00+00:00https://meowni.ca/posts/web-fonts<ul>
<li>
<span style="color: white;">phantom
<span style="border-bottom:1px solid black;">underlines</span>. isn't this
<span style="border-bottom:1px solid black;">amaaaaaazing.</span>
</span>
</li>
<li><span style="color: white;">i love waiting for 8 seconds and seeing this.</span></li>
<li>
<span style="color: white;">look at it. srsly.
<span style="border-bottom:1px solid black;">looooook</span>at it.
</span>
</li>
</ul>
<p>I spent a week traveling around Taiwan, on my awesome free roaming 2G data plan, and friends,
we need to talk about your web fonts. Also cats. They really love cats there.
Anyway, the thing about 2G is that I fully understand
that it will take me 10 seconds to load a page. What sucks is the fresh rage of
the following 4 seconds
where instead of content I get phantom underlines, waiting for
a slightly-different-sans-serif to download.</p>
<p>Listen: it doesn’t have to be this way. You can lazy load your font. It’s 4 lines
of JavaScript. 7 if you’re being ambitious.</p>
<h2 id="why-should-you-care">Why should you care</h2>
<p>I’ve been brainwashed to <em>really</em> care about first paint performance (thanks Chrome Dev Rel 😘),
and I’ve become a big fan of the “do less & be lazy” approach to building things.
What this means is that if something is not on your critical path, it probably doesn’t
need to be the first thing you paint on a page.</p>
<p>Now think about fonts: is the critical path <em>showing</em> text, or <em>styling</em> it? I’d
argue that unless your app is in the 1% it’s-all-a-magical-visual-experience bucket (in which case
this post is not for you), or we’re just talking about the fancy title on your site (which fine, can
be slow to paint or whatever),
it’s probably trying to communicate some content, and ugly content (that you prettify after) is better than no content.</p>
<p>(Real talk: if you don’t think rendering text is a critical path, you’re whack and we need to have a chat.)</p>
<p>There are two things you can run into when loading a web font:</p>
<ul>
<li><strong>FOIC</strong> (“flash of invisible content”) – when your browser sees that
you’re trying to use a font it doesn’t have it paints all the text in
invisible ink, waits, and when it finally gets the font, it re-paints and re-layouts the text correctly.
<a href="https://cloud.githubusercontent.com/assets/1369170/19876828/0aa7d0d6-9f97-11e6-86c8-b7e2c80a9986.gif">[see a gif of this]</a></li>
</ul>
<p>I hate this with the fire of a thousand suns, because instead of looking at actual content,
I’m looking at bullets and underlines and random text you forgot to style. Neat-o.</p>
<ul>
<li><strong>FOUC</strong> (“flash of unstyled content”) – Chrome stops waiting for a web font after 3 seconds (and, recently, after 0 seconds on 2G). What this means is instead of showing you invisible ink, it paints the text in your fallback
font. When your web font is finally downloaded, it then re-paints the already displayed text with the new font.
<a href="https://cloud.githubusercontent.com/assets/1369170/19876827/0aa5c8d6-9f97-11e6-81a2-13fa35f6bbc9.gif">[see a gif of this]</a></li>
</ul>
<p><strong>Side note</strong>: on iPhones, this timeout doesn’t exist, so you basically only get a FOIC – you wait the entire
time to get from “no text” to “all the text”, with no intermediate bail out state.</p>
<p>(<a href="http://output.jsbin.com/felocuh">Here</a> is the code that I used for these demos,
with GPRS and 2G throttling respectively in Chrome. This demo will look super snappy
on LTE. Everything is super snappy on LTE.)</p>
<h2 id="reading-material">Reading material</h2>
<p>A lot of people have written about web fonts, and I’m not trying to re-write their
posts. Chrome in particularly has been working a lot on improving this, by
decreasing the web font download timeout to 0s on 2G, and working on the <code class="language-plaintext highlighter-rouge">font-display</code> spec.</p>
<p>Here are some links I like:</p>
<ul>
<li>the <a href="https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/webfont-optimization">anatomy of a web font</a> and the
<a href="https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/webfont-optimization#webfonts_and_the_critical_rendering_path">dance</a> that a browser does to use a web font</li>
<li><a href="https://developers.google.com/web/updates/2016/02/font-display"><code class="language-plaintext highlighter-rouge">font-display</code></a>
options, and how it affects how fonts load</li>
<li><a href="https://groups.google.com/a/chromium.org/forum/m/#!msg/blink-dev/7s4-eQTAxqs/SoahsGpMAQAJ"><code class="language-plaintext highlighter-rouge">font-display: optional</code></a> and why it’s awesome (tl; dr: if you can’t do it fast, don’t do it at all)</li>
<li>minimizing <a href="https://jakearchibald.com/2014/minimising-font-downloads/">font downloads</a> by limiting
the range of characters you’re loading</li>
<li>why we should care about web fonts and how to minimize FOIT using JavaScript and a library called <a href="http://helenvholmes.com/writing/type-is-your-right">Font Face Observer</a></li>
<li>voltron solution <a href="https://jeremenichelli.github.io/2016/05/font-loading-strategy-static-generated-sites/">combining</a> FontFaceObserver, async loading a font bundle and web storage</li>
</ul>
<h2 id="lazy-loading-a-font">Lazy loading a font</h2>
<p>Personally, I would use <code class="language-plaintext highlighter-rouge">font-display: optional</code> everywhere, but that doesn’t really work anywhere yet.
In the meantime, here are 2 super simple ways to lazy load a web font.
Again, I don’t really mind having a FOUC, since it feels like progressive enhancement to me:
display the content as soon as you can, and progressively style it after.</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><head></span>
<span class="nt"><style></span>
<span class="nt">body</span> <span class="p">{</span>
<span class="nl">font-family</span><span class="p">:</span> <span class="s2">'Arima Madurai'</span><span class="p">,</span> <span class="nb">sans-serif</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt"></style></span>
<span class="nt"></head></span>
<span class="nt"><body></span>...<span class="nt"></body></span>
<span class="nt"><script></span>
<span class="c1">// Do this only after we've displayed the initial text.</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">url</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">https://fonts.googleapis.com/css?family=Arima+Madurai:300,400,500</span><span class="dl">'</span><span class="p">;</span>
<span class="nx">loadFont</span><span class="p">(</span><span class="nx">url</span><span class="p">);</span> <span class="c1">// hold tight, i tell you below.</span>
<span class="p">}</span>
<span class="nt"></script></span>
</code></pre></div></div>
<p>There’s basically two ways in which you can implement that <code class="language-plaintext highlighter-rouge">loadFont</code>:</p>
<h3 id="load-the-stylesheet-blocking">Load the stylesheet (blocking)</h3>
<p>This is the simplest way and works great for a simple page. But! Since loading/parsing
a stylesheet blocks parsing/painting, this doesn’t play nicely if you’re loading a bunch
of other modules after the document has loaded, since they will be delayed. [<a href="http://output.jsbin.com/cijokog">demo</a>]</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">link</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">link</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">link</span><span class="p">.</span><span class="nx">rel</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">stylesheet</span><span class="dl">'</span><span class="p">;</span>
<span class="nx">link</span><span class="p">.</span><span class="nx">href</span> <span class="o">=</span> <span class="nx">url</span><span class="p">;</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">head</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">link</span><span class="p">);</span>
</code></pre></div></div>
<h3 id="xhr-the-stylesheet-asynchronous">XHR the stylesheet (asynchronous)</h3>
<p>If you care about synchronicity (and tbh you probably should), you can do an async
XMLHttpRequest and create a style node with the result. [<a href="http://output.jsbin.com/veqiyuy">demo</a>]</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span>
<span class="nx">xhr</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="dl">'</span><span class="s1">GET</span><span class="dl">'</span><span class="p">,</span> <span class="nx">url</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
<span class="nx">xhr</span><span class="p">.</span><span class="nx">onreadystatechange</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">readyState</span> <span class="o">==</span> <span class="mi">4</span> <span class="o">&&</span> <span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span> <span class="o">==</span> <span class="mi">200</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">style</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">style</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">style</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">xhr</span><span class="p">.</span><span class="nx">responseText</span><span class="p">;</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">head</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">style</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="nx">xhr</span><span class="p">.</span><span class="nx">send</span><span class="p">();</span>
</code></pre></div></div>
<p>For bonus points, you can take this <a href="https://github.com/GoogleChrome/devsummit/blob/master/scripts/utils.js#L34">one step further</a> and
rather than creating an inline <code class="language-plaintext highlighter-rouge"><style></code>,
append a <code class="language-plaintext highlighter-rouge"><link></code> like in the previous method, since the browser cache is already
primed. If you trust your browser cache. I trust no one.</p>
<p>This is obviously not perfect. It will give you a FOUC on a fast LTE connection,
even though if you did nothing, like in the first demo, it wouldn’t. The point is that
not all of your audience is on an LTE connection, and I want you to think about them
when you’re working on a site. If you want to
minimize this FOUC, <a href="https://twitter.com/helenvholmes">Helen Holmes</a> gave an AMAZING <a href="https://www.youtube.com/watch?v=emLfXChvVPQ">talk</a> recently about web typography and performance, where she mentions how you
can try to match the x-heights of your fallback font to your target font, so that the FOUC is gentler.</p>
<p><strong>Update</strong>: I’ve built a <a href="https://meowni.ca/font-style-matcher/">font-style-matcher</a> that lets you do this matching of the x-heights and widths of the web font and fallback font! Go check it out, it’s preeeeetty sweet.</p>
<h2 id="tl-dr">TL; DR</h2>
<p>Web fonts are okay. They make your blog prettier. They’re also slow and kind of an
annoying experience, but if you need to use them, use them. Just remember that it’s
also your responsibility to make your site load super fast, and if you don’t,
it’s totes fair game for people (me) to whine about it on Twitter.</p>
<p><br /><br /></p>
<p>(🍹 to <a href="https://twitter.com/aerotwist">Paul Lewis</a> who had to sit through all
my questions and explain basic browser things to me. Again.)</p>
I made a 2001-era emoji font! That you can use!2016-10-04T00:00:00+00:00https://meowni.ca/posts/og-emoji-font<p>You know the scenes in Friends when Ross starts talking about dinosaurs
and he’s SUPER excited but everyone else is losing the will to live?
This is basically that, only instead of dinosaurs, it’s emoji, and unlike Ross,
I have never successfully befriended a monkey.</p>
<p>Last month, my coworker casually told me he still has a 2001 era DoCoMo phone, which is
one of the first phones to have emoji (🤓🤓🤓: emoji first appeared in 1999, on DoCoMo phones,
and DoCoMo phones alone). So I got ahold of this phone. Which charged, turned on
and most importantly, TOTALLY had OG emoji:</p>
<div style="width:100%">
<img src="/images/2016-09-28/all-1.jpg" width="30%" style="display:inline" />
<img src="/images/2016-09-28/all-2.jpg" width="30%" style="display:inline" />
<img src="/images/2016-09-28/all-3.jpg" width="30%" style="display:inline" />
<br /><br /><br />
</div>
<p>I spent the whole day being unproductive and sending emoji messages to people:</p>
<div style="width:100%">
<img src="/images/2016-09-28/msg-1.jpg" width="30%" style="display:inline" />
<img src="/images/2016-09-28/msg-2.jpg" width="30%" style="display:inline" />
<img src="/images/2016-09-28/msg-3.jpg" width="30%" style="display:inline" />
</div>
<h2 id="svgs">SVGs</h2>
<p>I then took a 10 hour flight to Europe and, for lack of better things to do
while watching every movie that came out this year, I drew every one of those emoji as a sprite.
166 emoji in total, 12x12px each, in one of six colors. This was my first time doing pixels
sprites, so I obviously fucked it up: I ended up with a bunch of random sprite sheets,
each with a random number of sprites in it, which was a bit of a mess. Thankfully,
<a href="https://twitter.com/amandaglosson">Amanda Glosson</a>, reigning queen of pixels, wrote me a script to transmogrify my mess into
individual svgs. <strong>These</strong> individual SVGs, to be exact:</p>
<iframe src="/images/2016-09-28/svgs.html" width="100%" onload="this.height=this.contentDocument.body.getBoundingClientRect().height + 20" frameborder="0"></iframe>
<p>LOOK HOW PRETTY THEY ARE! <span class="og">💓</span></p>
<h2 id="a-wild-font-appears">A wild font appears</h2>
<p>The reason why I made those SVGs was partly because Captain America: Civil War is unbearably
boring, but partly because I wanted to make a font and use it everywhere like
an emoji hipster.</p>
<p>So then I did. I used <a href="http://app.fontastic.me/">Fontastic</a> to make the font – it’s black and white, because at
the time of writing this, colour fonts weren’t supported on the web (tbh, even a year later, when colour
fonts <em>are</em> supported, I still couldn’t tell you how to make on). I
also mapped the original emoji glyph to one of the current existing emoji code points,
based on <a href="http://unicode.org/emoji/charts/full-emoji-list.html">this</a> list, because let’s be honest, some of them were mysterious.
Do you know what <span class="og">💥</span> means? It’s 💥. And
<span class="og">💦</span> is 💦. 12 pixels ain’t a lot of pixels, friends.</p>
<style>
.emoji-sample {
font-size: 24px;
letter-spacing: 6px;
line-height: 30px !important;
word-wrap: break-word;
}
</style>
<p>Here the 166 emoji as they look today:</p>
<div>
<p class="emoji-sample">❤💔💓💕😃😖😞😵😠🎵♨💠💋✨💡💢👊💣🎶💤❗⁉‼💥💦💧💨〰️➰⤴⤵↗↘↖↙☀️☁️☔️⛄⚡️🌀🌁🌂♈️♉️♊️♋️♌️♍️♎️♏️♐️♑️♒️♓️🎽⚾️⛳🎾⚽️🎿🏀🏁📟🚃Ⓜ🚄🚗🚙🚌🚢✈️🏠🏢🏣🏥🏦🏧🏨🏪⛽🅿🚥🚻🍴☕🍸🍺🍔👠✂️🎤🎥🎠🎧🎨🎩🎪🎫🚬🚭📷👜📖🎀🎁🎂☎︎📱📝📺🎮💿♥♠♦♣👀👂✊✌️✋👣👟👓🌑🌔🌓🌙🌕🐶🐱⛵🎄📲📩📠✉︎💴🆓🆔↩🆑🔍🆕🚩➿#️⃣0️⃣1️⃣2️⃣3️⃣4️⃣5️⃣6️⃣7️⃣8️⃣9️⃣🆗</p>
</div>
<p>And here’s the same list, using the DoCoMo emoji font:</p>
<div>
<p class="og emoji-sample">❤💔💓💕😃😖😞😵😠🎵♨💠💋✨💡💢👊💣🎶💤❗⁉‼💥💦💧💨〰️➰⤴⤵↗↘↖↙☀☁︎☔︎⛄⚡︎🌀🌁🌂♈︎♉︎♊︎♋︎♌︎♍︎♎︎♏︎♐︎♑︎♒︎♓︎🎽⚾︎⛳🎾⚽︎🎿🏀🏁📟🚃Ⓜ🚄🚗🚙🚌🚢✈️🏠🏢🏣🏥🏦🏧🏨🏪⛽🅿🚥🚻🍴☕🍸🍺🍔👠✂︎🎤🎥🎠🎧🎨🎩🎪🎫🚬🚭📷👜📖🎀🎁🎂☎︎📱📝📺🎮💿♥♠♦♣👀👂✊✌︎✋👣👟👓🌑🌔🌓🌙🌕🐶🐱⛵🎄📲📩📠✉︎💴🆓🆔↩🆑🔍🆕🚩➿#0123456789🆗</p>
</div>
<p>There’s some OG emoji that don’t even exist today!:</p>
<p class="og emoji-sample">abcde</p>
<h2>Boom! <span class="og" style="font-weight:normal;">💣</span></h2>
<p>If you want to use it, you can download the font <a href="/fonts/og-dcm-emoji.ttf">here</a>, and use
it as a <code class="language-plaintext highlighter-rouge">font-face</code>:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@font-face {
font-family: og-emoji;
src: url(/fonts/og-dcm-emoji.ttf);
}
.og {
font-family: og-emoji, sans-serif;
}
<span class="nt"><span</span> <span class="na">class=</span><span class="s">'og'</span><span class="nt">></span>💥<span class="nt"></span></span>
</code></pre></div></div>
<p>If you’re going to use it, maybe give me some credit, because I spent an unhealthy
amount of time on it. Also, if you’re
DoCoMo, please don’t sue me. Emojineering comes only from the <span class="og">❤</span>.</p>
<h1 class="og" style="font-weight:normal;">✌︎ ✨ 🐱 💋 🆗</h1>
Emoji: how do you get from U+1F355 to 🍕?2016-04-04T00:00:00+00:00https://meowni.ca/posts/emoji-emoji-emoji<p>You know that scene in The Rock where Nicolas Cage is super dreamy (like he is)
and decides his life mission is to look for VX poison gas and save San Francisco (like he would)?
That’s baaaasically me, if by “look for VX poison gas” you mean “nerd out on emoji”, and
by “save San Francisco” you mean “and tell everyone about it”.
I mean, you clicked on this link, what did you think was going to happen?</p>
<h2>🍿 How did we get so lucky?</h2>
<p>An <a href="https://en.wikipedia.org/wiki/Emoji">emoji</a> is a coloured <a href="https://en.wikipedia.org/wiki/Glyph">glyph</a>. They appeared around 1999 in Japan, where each mobile carrier implemented their own variants, and people
were sending them around in text messages. This was a bit of a mess, as
you can imagine proprietary formats interacting with other proprietary formats to be, so in 2000
there was a proposal to standardize them. It wasn’t until 2009, though, that emoji got specced
in Unicode 5.2 <span style="color:#7ccdea;">#blessed</span>.</p>
<p><a href="http://unicode.org/reports/tr51/">Spec</a> trivia: each emoji has a <a href="http://unicode.org/reports/tr51/#Design_Guidelines">design guideline</a>
and name, which is a description/suggestion of what the
emoji should look like. This is why 💁,for example, often gets in trouble for being
labelled as <em>Information Desk Person</em>, but is actually just a sassy lady: it’s the
implementation of the emoji that doesn’t match its original description, not the
other way around. If you take sassy lady away from me though, there will be words.</p>
<p>My favourite description is
<em>Clockwise Rightwards and Leftwards
Open Circle Arrows With Circled One Overlay</em> (or 🔂 for short), which shows true dedication to typing.</p>
<p>Emoji does not have a plural in Japanese, so stop trying to make <em>emojis</em> happen.</p>
<h2 style="border-left-color:#fbcd46;">🙀 What is an emoji even</h2>
<p>Every emoji is represented by a <code class="language-plaintext highlighter-rouge">code point</code> (a hexadecimal number, zero-padded up to at least four digits, like U+26C4).
Because all JavaScript strings are internally (i.e. in browsers) represented in UTF-16, this means that each <a href="https://en.wikipedia.org/wiki/Code_point">code point</a>, in turn, can be represented by one or more 16-bit <code class="language-plaintext highlighter-rouge">code unit</code>.</p>
<p>Some emoji are boring (or in the <a href="https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane">basic</a> unicode plane), which means one glyph is represented by one <code class="language-plaintext highlighter-rouge">code unit</code>.
☃ for example is <code class="language-plaintext highlighter-rouge">U+2603</code> (you’d write this as <code class="language-plaintext highlighter-rouge">\u2603</code> in the codes). In JavaScript, to find out how many code units represent an emoji, you can query its length:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"☃".length // returns 1
"🐼".length // returns 2
</code></pre></div></div>
<p>To find out what the code units actually are, you can look them up:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"☃".charCodeAt(0).toString(16) // returns 2603.
"🐼".charCodeAt(0).toString(16) // returns d83d
"🐼".charCodeAt(1).toString(16) // returns dc3c
</code></pre></div></div>
<p>Let’s talk about panda! 🐼 lives in the “astral” plane (it’s officially
called a <a href="https://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Multilingual_Plane">supplementary</a> plane, but that’s boring), which means its
code point has more than four digits, and is represented by <em>two</em> code units. This
is called a <code class="language-plaintext highlighter-rouge">surrogate pair</code>. As we saw above, 🐼 is made up of two
surrogates, <code class="language-plaintext highlighter-rouge">U+D83D</code> and <code class="language-plaintext highlighter-rouge">U+DC3C</code>.</p>
<p>My favourite emoji (thank you for asking!) is the dancer from the Android set. Look
at this blob. Look at all the shits it doesn’t give. It’s so happy. We should all be like this blob.</p>
<p><img width="60" alt="the dancer as implemented on android, a beautiful blob with a rose in its teeth" src="https://cloud.githubusercontent.com/assets/1369170/14198590/c07a7d14-f790-11e5-9d95-499731513ab3.png" /></p>
<h2 style="border-left-color:#f19fd9;">🙋 What about emoji modifiers?</h2>
<p>🇨🇦 and 👍🏿 and 👨👨👧👧 are also
“astral” plane emojis, only they’re made out of 2+ surrogate pairs:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"👍🏿".length // returns 4
"🇨🇦".length // returns 4
"👨👨👧👧".length // lol returns 11 (2*4 for each person + 3 connectors)
</code></pre></div></div>
<p>If you type this (in April 2016) in something like Atom (or even Atom’s dev tools) though,
you’ll notice something weird. Instead of getting a black thumbs up, or the Canadian flag, you get this (I had to highlight the Canadian flag bit, because the glyphs are white):</p>
<p><img width="129" alt="a yellow thumbs up with a dark brown square; two boxes, each with the letters C and A; 4 separate heads in a line" src="https://cloud.githubusercontent.com/assets/1369170/14193347/def54478-f758-11e5-95ca-bc8b5988874c.png" /></p>
<p>Whoaaaa, what’s going on there? (This is a trick question. I’ma tell you what’s going on there.)</p>
<p>The <a href="http://unicode.org/reports/tr51/#Flags">flags</a> are built around a weird (and annoying to implement) rule: the
surrogate pairs (called <code class="language-plaintext highlighter-rouge">regional indicators</code>) spell out the country code (so
🇨🇦 is actually <code class="language-plaintext highlighter-rouge">[C][A]</code>). Skin colours are similar, but a little simpler:
they’re made out a special emoji <a href="http://unicode.org/reports/tr51/#Subject_Emoji_Modifiers">base</a> + one of the 6 special colour <a href="http://unicode.org/reports/tr51/#Emoji_Modifiers_Table">modifiers</a>. The couples/multi
families are a <a href="http://www.unicode.org/emoji/charts/emoji-zwj-sequences.html">sequence</a> of characters, that together make one emoji.</p>
<h2 style="border-left-color:#a77be3;">👾 So what does Chrome do?</h2>
<p>Okay, cool! We figured out what code units we need for 🇨🇦, now, let’s figure
out how to render them!</p>
<p>First, Chrome uses a text shaper called <a href="http://harfbuzz.org/">Harfbuzz</a>. Text shapers
take Unicode code points and convert them to glyph indices (basically saying “you’re going to
have to draw glyphs 23 and 74”) – and guess what we have! Unicode
code points! The text shaper is the one that knows how to look at this stream
of code units and surrogate pairs and figure out which are standalone, which
are weirdo flags, and which are modifiers. Once it’s done with it, it comes
up with the glyph and the position where to draw it. If you think about a couple,
👩❤️👩, all surrogate pairs need to be drawn on
top of each other, so that the spacing around the final glyph adds up.</p>
<p>This glyph and its size/position eventually goes to <a href="https://en.wikipedia.org/wiki/Skia_Graphics_Engine">Skia</a>,
Chrome’s graphics engine. It is the one that paints the right thing on the screen (<a href="https://code.google.com/p/chromium/codesearch#chromium/src/third_party/skia/src/ports/SkFontHost_mac.cpp&l=1257">here</a> is that code).</p>
<h2 style="border-left-color:#5b86f7;">🖌 What about fonts?</h2>
<p>Fonts, boy, them’s a pickle. There’s basically one font per platform that
actually knows how to draw emoji (unless you went out of your way to
install extra ones). All the other fonts just rent the emoji from it.
These fonts are AppleColorEmoji (OS X), Segoe UI Symbol/Emoji (Windows),
NotoColorEmoji (Android) and I don’t know what Linux does, but it’s probably
black and white and who cares, I hear you can run bash on Windows now. I’m going to keep talking about the Apple font, because that’s
the code path I worked on in Chrome, but Windows works very similarly.</p>
<p>So let’s say you have this:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><p</span> <span class="na">style=</span><span class="s">"font-family: 'Comic Sans MS', sans-serif;"</span><span class="nt">></span>😻<span class="nt"></p></span>
</code></pre></div></div>
<p>Chrome (specifically <a href="https://en.wikipedia.org/wiki/Blink_(web_engine)">Blink</a>) will first look up the glyph corresponding to 😻 in the Comic Sans font.
It won’t find it, so it will first try the web <code class="language-plaintext highlighter-rouge">fallback</code> font, the default
platform sans-serif (I think on OS X this is Helvetica, and it’s probably
Arial on Windows). That also doesn’t have the glyph (remember, only one font
knows how to draw cats with heart eyes), so Chrome knows to fallback to
<code class="language-plaintext highlighter-rouge">AppleColorEmoji</code> by looking at the glyph: it’s 32 bits and it has an emoji presentation,
so it must be an emoji. <a href="https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm&q=fontcachemac&sq=package:chromium&type=cs&l=91">Here</a>’s the code
where that happens (the real work is done <a href="https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/platform/fonts/SymbolsIterator.cpp&q=FontFallbackPriority::EmojiEmoji&sq=package:chromium&type=cs&l=23">here</a> and <a href="https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/platform/text/CharacterEmoji.cpp&q=characteremoji.cpp&sq=package:chromium&type=cs&l=95">here</a>. This entire last file is pretty glorious and useful if you ever need to know if a thing is an emoji or not).</p>
<p>Cool, so now Chrome knows to ask AppleColorEmoji for the cat, takes that glyph,
passes it to Skia, and paints it
in the right position, and everything is fine. Cool cool cool.</p>
<p>Remember though how in Atom, you see <img width="53" alt="a yellow thumbs up with a dark brown square" style="display:inline-block;" src="https://cloud.githubusercontent.com/assets/1369170/14195194/5704b2a2-f76b-11e5-922c-d4753861d55f.png" />
instead? What’s up with that?
Atom is built on Chromium soooo it should work, right?</p>
<p>Well as we know, software. This fallback logic I just mentioned was a bit
broken pre Chrome 50 for flags and modifiers and complicated emoji like that.
So Chrome got as far as figuring out that there were two different glyphs,
“thumbs up” and “skin colour”, but not how to fallback to the correct font
and draw the compound “black thumbs up” glyph. So that’s why you got them separately. That’s
been fixed now! Yay!</p>
<h2 style="border-left-color:#ed2f20;">💥🙌✨💝</h2>
<p>Congratulations! Now you too can be Nicolas Cage and shout at people about
emoji trivia! Wasn’t this fun?</p>
<p><img src="https://media.giphy.com/media/RrVzUOXldFe8M/giphy.gif" alt="nicolas cage y'all" /></p>
I fixed a pair of headphones with some soldering, and you can too!2016-01-26T00:00:00+00:00https://meowni.ca/posts/how-i-fixed-headphones<p>Here’s the thing: I have this <a href="http://s3-us-west-2.amazonaws.com/hypebeast-wordpress/image/2008/07/ed-banger-wesc-headphones.jpg">sweet pair of headphones</a> that I got from a
friend a gazillion years ago. I’ve always liked them because they were
free and they look super quirky but it turns out they were a limited
edition done for Ed Banger Records, which is the label that produces
Justice and Mr. Oizo. It also turns out the wire on the side is getting
a little fuckety, and is probably going to break soon, and my sweet, free, limited
edition headphones are going to be busted. 🎧</p>
<p>Here’s this other thing you need to know: I am clumsy, and pretty shit at “fixing things”.
If I could do it, you could do it, and I’m gonna tell you how.</p>
<h2 id="step-0-you-should-figure-out-whats-wrong">Step 0: you should figure out what’s wrong</h2>
<p>I think about 4 things can go wrong with headphones:</p>
<ol>
<li>one of the cables attached to the headphones speakers is broken</li>
<li>the jack is broken</li>
<li>there’s a hole somewhere in the middle of the cable, most likely because your asshole cat went to town on it</li>
<li>the speakers are completely busted</li>
</ol>
<p>№ 4 is A Hard Problem™, and it requires a level of skill I
don’t have. № 1 involves re-attaching wires to speakers, and
that’s what this post is about. № 2 and 3 are basically a combination of buying
a new cable and re-attaching it to the speakers, which is № 1 again. Which
means you can basically fix 3/4 problems with headphones 💖.</p>
<h2 id="step-1-pop-the-trunk">Step 1: pop the trunk</h2>
<p>After you’ve figured out which side of your headphones is broken, take the squishy earpad off and figure out how to get inside. Some headphones have little lever
things you need to pop. Mine have 3 little screws. Unscrew them, and place them somewhere where your asshole cat can’t eat them.</p>
<p>This is what it looked like inside:
<img src="https://cloud.githubusercontent.com/assets/1369170/12541832/958b0672-c2d0-11e5-80f7-1526fa395c72.jpg" alt="inside of the headphones" /></p>
<h2 id="step-2-the-wires">Step 2: the wires</h2>
<p>Hopefully just one of the wires came loose, so that you don’t have to
guess about which wire goes where. If it’s just the one, move to the next step.
If, like me, you have to cut the whole cable and resolder all of them, you probably (definitely) should write down what order the wires came in, because you’ll forget.
I had this (if you look at the post-soldering photo, this might even make sense):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// i had a thicker and thinner cables.
// thick-orange means the orange wire of the thick cable.
// thin-orange is the orange wire of the thinner cable.
------------- speaker ---------
[ ] [ ] [ ] [ ] soldered together
| | | | | |
| | | | | |
thick-orange thin-orange Ø thick-red thick-blue thin-blue
</code></pre></div></div>
<p>I also took a picture. It ended up being blurry and useless, so the written bit was 👌.</p>
<h2 id="step-3-cut-some-wires">Step 3: cut some wires</h2>
<p>If the cable you’re trying to solder is too frayed, you might have to shorten it.
You might also have to <a href="http://www.instructables.com/id/Use-scissors-to-strip-wires!/">strip</a> the cable if there isn’t enough available.</p>
<p>After you’re done, you should have a bunch of wires fairly similar to the
ones already attached to the speaker. I stripped mine with a kitchen knife because
I don’t own any tools, and it looked like this [read: pretty pro]:</p>
<p><img src="https://cloud.githubusercontent.com/assets/1369170/12541647/804d4b14-c2ce-11e5-9d98-ed2b5923e87c.jpg" alt="stripped wire" /></p>
<h2 id="step-4-they-see-me-solderin-they-hatin">Step 4: they see me solderin’, they hatin’</h2>
<p>And that’s because I’m hilariously bad at it.</p>
<p>Anyway, here’s a super verbose <a href="https://www.youtube.com/watch?v=BLfXXRfRIzY">video</a>
on soldering. It basically involves heating the iron, melting some
copper into a liquid ball (if there isn’t already enough on there), and using it to attach your wire to the
speaker end it needs to be in. While it’s hard to be really good at soldering,
it’s surprisingly easy to be averagely bad at it.</p>
<p>The good news is that if
you fuck it up, you can just unsolder the bit you just did with more heat,
and start again. Just practice a little on some spare wires before you go full out on your headphones, and don’t burn yourself.</p>
<p>You’re going to first unsolder the old wires off the speaker, and re-solder
your new wires on. Here’s what mine looked like post soldering. You’ll notice
it’s messy and that nobody cares, because it’s all hidden inside the headphones
anyway. All you care about is your wire making contact with the speaker.</p>
<p><img src="https://cloud.githubusercontent.com/assets/1369170/12542003/6ee0c410-c2d2-11e5-9858-e801279b3ee7.jpg" alt="photo of speaker after soldering" /></p>
<h2 id="step-5-the-reckoning">Step 5: the reckoning</h2>
<p>You should plug in your headphones. If everything went great, then sound
should come out of them!</p>
<p>If sound isn’t coming out of them, it could be that
your connection is loose and you need to debug your soldering (which means repeating step 4), or that
it wasn’t the wires after all 😓</p>
<p><strong>Important bit I missed the first time:</strong> The cable sits in a little rubber casing
at the edge of the headphone like this:</p>
<p><img src="https://cloud.githubusercontent.com/assets/1369170/12625281/f4b8189c-c4e7-11e5-898e-ac9d8ed5011c.jpg" alt="cable rubber casing" /></p>
<p>When I first took apart the headphones, I thought it was annoying the cable was glued to this rubber casing, because it meant I couldn’t use it, and had to find a new one (hoarding broken headphones helps with this). Spoilers: this was
intentional. You should ALSO super glue your cable inside the
rubber casing, or else when you invariably tug on the cable, it will totally
break the soldering you just did. Possibly just before you’re about to demo
your amazing new fixed headphones to your friends.</p>
<p>To be honest, the most annoying thing was putting the little earpads back. After serious
struggling, I discovered there’s a little wedge on the headphones base, where you can slip a bit of the earpad in,
and then slide it all the way across. Yours might have this too!</p>
<h2 id="you-did-it-yay">You did it! Yay!</h2>
<p>If everything went right, you’ve just fixed a pair of headphones.
High five, you hardware hacker you! I’m so proud! 👍💖</p>
2015: a year in review2015-12-22T00:00:00+00:00https://meowni.ca/posts/a-year-in-review<p>I’ve never really done a year in review. One day, I’d like to <a href="https://github.com/una/personal-goals">open source</a>
my goals, but since I’m still a chicken, this is a baby step towards that. Plus, this is
one of the first years I’m really proud of, and things that you’re proud of tend
to live on the Internet, for posterity.</p>
<p>Here’s what my GitHub contributions say about it:</p>
<p><img alt="2015 contribution graph" src="https://cloud.githubusercontent.com/assets/1369170/12084003/d0f4558a-b261-11e5-84f4-d28e947dd75d.png" /></p>
<h2 id="burning-out">Burning out</h2>
<p>The year started off really poorly. My team had just shipped the new Profiles
UI in Chrome, after a year and a half of hard work, and it was met with a looooot
of Internet anger. On one side we had data to prove that the change we did was right, which made
the powers that be want to stick by it; on the other side I had Twitter,
who was calling me names and wanting me fired. Kind of ironic, since
I was just the person who implemented the feature and had no power
to change it.</p>
<p>I think what burnt me out wasn’t waking up to a stream of negative
emails and tweets, it was knowing that there was absolutely nothing I could do about
it other than wait.</p>
<p>So I started working on dumb side projects to feel better. I made a
<a href="http://meowni.ca/meowto/">link aliaser</a>. I bought <a href="http://canihaveapony.com/">dumb</a>
domains. I wrote <a href="http://meowni.ca/posts/chromium-101/">blog</a> <a href="http://meowni.ca/posts/chromium-owners">posts</a>
about the only thing that I knew, which was working on Chromium.
I noticed that not working on Chromium made me happy.</p>
<p>So I bit the bullet, left Chromium, and joined Polymer.</p>
<h2 id="joining-polymer">Joining Polymer</h2>
<p>Looking back, I picked Polymer for a bunch of silly reasons that ended up working out
spectacularly well. I wanted to leave Montreal. I wanted to work on JavaScript,
since it was the only thing keeping me going. I didn’t want to commute to Mountain View,
which reduced my options by like a billion percent, and I wanted to ship things.
Polymer had all of that. So on April 15, I packed my cat and my books
and moved to San Francisco.</p>
<p>Polymer is my dream job. I get to write code
that I’m genuinely passionate about. I get to try to change the web platform, and talk
about why I think we’re doing the right thing. Most importantly, I get to ship
something everyday. It turns out that’s a thing that matters to me a lot.</p>
<p>I miss working on Chromium. I miss C++ and the big-ness and complicated-ness
that is working on a browser. It taught me that if I could find my way around
the 7 million lines of code and actually do something useful, there’s basically
nothing that I can’t do or learn. That’s one of the best feelings.</p>
<h2 id="tl-dr">TL; DR.</h2>
<p>I shipped 5 projects:</p>
<ul>
<li><a href="http://meowni.ca/meowto/">meowto</a></li>
<li><a href="http://meowni.ca/emoji-translate/">emoji-translate</a></li>
<li><a href="https://caturday-post.herokuapp.com/">caturday post</a></li>
<li><a href="http://meowni.ca/emoji-rain/">emoji-rain</a></li>
<li><a href="http://meowni.ca/muster/#6&23">muster</a></li>
<li><a href="http://meowni.ca/is-ie10-dead/">is IE10 dead</a></li>
</ul>
<p><br />
I gave 5 different talks at 3 conferences and 2 meetups:</p>
<ul>
<li>Contributing to Chromium, <a href="https://speakerdeck.com/notwaldorf/contributing-to-chromium">slides</a></li>
<li>Push notifications for fun and profit, <a href="https://speakerdeck.com/notwaldorf/push-notifications-for-fun-and-profit-if-by-profit-you-mean-cats">slides</a>, <a href="https://vimeo.com/137771040">video</a></li>
<li>Styling the Shadow DOM without dragons, <a href="https://speakerdeck.com/notwaldorf/styling-the-shadow-dom-without-dragons">slides</a>, <a href="https://www.youtube.com/watch?v=IbOaJwqLgog">video</a></li>
<li>Input I <3 you but you’re bringing me down, <a href="https://speakerdeck.com/notwaldorf/input-i-3-you-but-youre-bringing-me-down">slides</a>, <a href="https://vimeo.com/144980655">video</a></li>
<li>How I didn’t fix emoji in Chrome, <a href="https://speakerdeck.com/notwaldorf/or-how-i-didnt-fix-emoji-in-chrome">slides</a></li>
</ul>
<p><br />
I wrote a blog post that a lot of people liked. Most importantly, this happened (#humblebrag):</p>
<blockquote class="twitter-tweet" lang="en"><p lang="en" dir="ltr">I think <a href="https://t.co/eAoYZGWieP">https://t.co/eAoYZGWieP</a> by <a href="https://twitter.com/notwaldorf">@notwaldorf</a> may be the best thing I have ever read</p>— Jeff Atwood (@codinghorror) <a href="https://twitter.com/codinghorror/status/657371215800086529">October 23, 2015</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p><br />
I had my first ever interview, on <a href="https://usesthis.com/interviews/monica.dinculescu/">The Setup</a>. I spoke on my
first ever podcast, <a href="http://hanselminutes.com/499/understanding-web-components-and-polymer-with-monica-dinculescu">Hanselminutes</a>.
I discovered <a href="https://www.instagram.com/p/9aW5ZxIrBu">#nailconf</a>, which reassures me I can be both a giant nerd and have pretty nails. I kept
3 succulents alive, accidentally killed one, and resurrected a fig leaf tree. I saw my first live
baseball and basketball games. I bought a second
<a href="https://www.instagram.com/p/-VIJ05orDH">ukulele</a>. I didn’t work on weekends. I didn’t spoil Star Wars for
anybody. I turned 30.</p>
<h2>🎉</h2>
<p>See y’all next year, internet friends!</p>
<input> I ♡ you, but you're bringing me down2015-10-22T00:00:00+00:00https://meowni.ca/posts/a-story-about-input<p>Some people build furniture. Some people knit. Some people have hobbies that
don’t involve HTML specs from the 90s. I am not those people. So here’s a story
about <code class="language-plaintext highlighter-rouge"><input></code>, how it got to be the jerk that it is, and why it needs to die in a fire.</p>
<h2 id="the-early-years">The early years</h2>
<p>1995 was a good year. Friends, ER, Xena were all on TV. TLC had dominated the
charts with “Waterfalls”. Browsers were ok, because HTML was pretty ok. We had
Mosaic, Netscape and IE1, and the <a href="http://tools.ietf.org/html/rfc1866">HTML2</a>
spec was finally getting around to standardizing forms. 1995 was the year
when <code class="language-plaintext highlighter-rouge"><input></code> was born, and now that it’s about old enough to drink, we need to have a talk.</p>
<p>Input initially came along with 8 types: <code class="language-plaintext highlighter-rouge">text</code>, <code class="language-plaintext highlighter-rouge">password</code>, <code class="language-plaintext highlighter-rouge">checkbox</code>, <code class="language-plaintext highlighter-rouge">radio</code>, <code class="language-plaintext highlighter-rouge">image</code>, <code class="language-plaintext highlighter-rouge">hidden</code>,
<code class="language-plaintext highlighter-rouge">submit</code> and <code class="language-plaintext highlighter-rouge">reset</code>, and in a separate <a href="https://www.ietf.org/rfc/rfc1867.txt">RFC</a> that followed, <code class="language-plaintext highlighter-rouge">file</code>.</p>
<p>Wait, did you say <em>image</em>? Yeah, let’s talk about it.</p>
<p><code class="language-plaintext highlighter-rouge"><input type="image" src="cat.png"></code> looks like an image, but it’s actually an image
button that also submits the (x,y) coordinates of where you clicked on the image. Unless you don’t specify
a <code class="language-plaintext highlighter-rouge">src</code> file, in which case it’s an “image button” that says “Submit”.
Unless you’re in Firefox, in which case it says “Submit Query” and looks like
a label. Unless you’re in
IE in which case it doesn’t say anything at all.</p>
<p><img src="/images/2015-10-22/type-image.png" alt="input type=image with no source" /></p>
<p>Also, for your local pub trivia night, the message that the <code class="language-plaintext highlighter-rouge">type=file</code> input to
indicate you haven’t done anything is “No file chosen”, “no file selected”,
“No file selected”, and just an empty textbox on Chrome, Safari, Firefox and IE respectively.</p>
<p>Right, ok.</p>
<h2 id="and-now-a-textarea-rant">And now, a <code class="language-plaintext highlighter-rouge"><textarea></code> rant</h2>
<p>I always thought <code class="language-plaintext highlighter-rouge">input</code> and <code class="language-plaintext highlighter-rouge">textarea</code> came at later dates, and that explained
why they’re kind of insanely different. This is kind of true, since <code class="language-plaintext highlighter-rouge">input</code> was
around in Mosaic since at least 1993, and it was a fixed-up implementation of
<code class="language-plaintext highlighter-rouge">ISINDEX</code>. However, on the record, they were both children of the HTML2 spec,
which decided that <code class="language-plaintext highlighter-rouge"><input></code> is a self closing tag and uses a <code class="language-plaintext highlighter-rouge">value</code> attribute,
while <code class="language-plaintext highlighter-rouge"><textarea></code> needs a closing tag and uses its contents, even though they
both just hold text that someone else has entered:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><input</span> <span class="na">value=</span><span class="s">"batman"</span><span class="nt">></span>
<span class="nt"><textarea</span> <span class="na">rows=</span><span class="s">"1"</span><span class="nt">></span>batman<span class="nt"></textarea></span>
</code></pre></div></div>
<p>Update: someone pointed out that <code class="language-plaintext highlighter-rouge"><textarea></code> needs to support multilines,
and newlines aren’t allowed inside attributes values, which is why it needed
to use its contents. Makes sense!</p>
<h2 id="1995-2011-the-slow-years">1995-2011, the slow years</h2>
<p>In 1999, <a href="http://www.w3.org/TR/html401/">HTML4</a> only added <code class="language-plaintext highlighter-rouge">type="button"</code>. My favourite part about
it is that with no custom styles, an <code class="language-plaintext highlighter-rouge"><input type="button"></code> and an
<code class="language-plaintext highlighter-rouge"><input type="button" value="Submit"></code> on the same line, do not align vertically
on Chrome/Safari/Edge.</p>
<p><img height="40px" alt="input type=button misalignment" src="/images/2015-10-22/button-align.png" /></p>
<h2 id="then-everything-got-worse">Then everything got worse</h2>
<p>Later, in 2011, the <a href="http://www.w3.org/html/wg/drafts/html/master/Overview.html">HTML5</a> spec added a billion new input types. It’s now 2015, and
most are not implemented. The TL; DR of the missing features is: <code class="language-plaintext highlighter-rouge">type=color</code> only
<a href="http://caniuse.com/#feat=input-color">works</a> on Firefox/Chrome, date/time
inputs only <a href="http://caniuse.com/#feat=input-datetime">work</a> on Chrome/Edge/iOS, and
everything that works on Chrome works on Opera. Here’s a
<a href="http://output.jsbin.com/mimuko">demo</a> of all of the different input types to date,
so that you can compare and sob by yourself.</p>
<p>Let’s talk about some interesting ones.</p>
<p><code class="language-plaintext highlighter-rouge"><input type="search"></code> has some arbitrary text padding, borders,
and badass mid-2000s-style rounded corners, all of which are inconsistent across all browsers, and almost
impossible to get <a href="http://tjvantoll.com/2013/04/15/list-of-pseudo-elements-to-style-form-controls/">rid</a> of.</p>
<p><img height="130px" alt="input type=search weird default styles" src="/images/2015-10-22/type-search.png" /></p>
<p>If you’re on a lucky browser that does support <code class="language-plaintext highlighter-rouge">type="date"</code>, don’t worry about
styling the date picker — there are 8 weirdo <code class="language-plaintext highlighter-rouge">::webkit</code> pseudo-selectors out there,
but they’ll only let you <a href="http://tjvantoll.com/2013/04/15/list-of-pseudo-elements-to-style-form-controls/">style</a>
the input textbox, and not the actual date dropdown. CSS is bad for your health anyway.</p>
<h2 id="just-when-you-thought-it-couldnt-get-any-worse-javascript">Just when you thought it couldn’t get any worse, JavaScript</h2>
<p>You see, I can justify CSS quirks. I worked on Chrome for 2 years,
I work next to the Blink team now, I understand we’re all writing different
renderers and they all have their own CSS bugs. However, the <code class="language-plaintext highlighter-rouge"><input></code> API isn’t
quirky — it’s literally just a jar of spiders, and the moment you open the jar,
it’s too late. You’re covered in spiders. Even your cat is a spider now. Better find
some fire.</p>
<p>Since 1995, inputs with type <code class="language-plaintext highlighter-rouge">radio</code> and <code class="language-plaintext highlighter-rouge">checkbox</code> have
had an extra attribute, <code class="language-plaintext highlighter-rouge">checked</code>, to determine their checked status. Since an <code class="language-plaintext highlighter-rouge">HTMLInputElement</code>
is an <code class="language-plaintext highlighter-rouge">HTMLInputElement</code> is an <code class="language-plaintext highlighter-rouge">HTMLInputElement</code>, this also means that all other
input types have this property; it just gets ignored. So even though it doesn’t
make sense, this is perfectly fine:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">textInput</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">'</span><span class="s1">input[type="text"]</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">textInput</span><span class="p">.</span><span class="nx">checked</span><span class="p">);</span> <span class="c1">// prints false.</span>
<span class="nx">textInput</span><span class="p">.</span><span class="nx">checked</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">textInput</span><span class="p">.</span><span class="nx">checked</span><span class="p">);</span> <span class="c1">// prints true.</span>
<span class="c1">// did not open the hellmouth.</span>
</code></pre></div></div>
<p>Cool. Cool cool cool.</p>
<p>Inputs also have text, and text can be selected, so the <code class="language-plaintext highlighter-rouge">HTMLInputElement</code>
prototype also defines two properties,
<code class="language-plaintext highlighter-rouge">selectionStart</code> and <code class="language-plaintext highlighter-rouge">selectionEnd</code> which are two numbers defining your selection
range. So you can do:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">'</span><span class="s1">input</span><span class="dl">'</span><span class="p">).</span><span class="nx">selectionStart</span> <span class="o">+=</span> <span class="mi">2</span><span class="p">;</span>
</code></pre></div></div>
<p>And advance the beginning of the text selection by 2 characters. Super pedestrian, except for
the fact that the <code class="language-plaintext highlighter-rouge">selectionStart</code> — and brethren — attribute is only <a href="https://html.spec.whatwg.org/multipage/forms.html#do-not-apply">available</a> for inputs
of type <code class="language-plaintext highlighter-rouge">text</code>, <code class="language-plaintext highlighter-rouge">url</code> and <code class="language-plaintext highlighter-rouge">password</code> and just accessing it (not even setting it)
throws an exception for all other types:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Uncaught DOMException: Failed to read the 'selectionStart'
property from 'HTMLInputElement': The input element's type ('number')
does not support selection.
</code></pre></div></div>
<p>Even though manually I can totally select that text:</p>
<p><img height="35px" alt="input type=number with selected text" src="/images/2015-10-22/type-number.png" /></p>
<p>So in some cases, irrelevant properties can be interacted with, but in
other cases they open the hellmouth. Neat-o. That’s just the kind of consistency
I look for in an API.</p>
<h2>🙈</h2>
<p>There’s more. I’m sure there’s more. The thing is, browsers have had <a href="http://www.martinrinehart.com/frontend-engineering/engineers/html/html-tag-history.html">21 years</a>
to sort out inputs, and they haven’t even managed to agree on how to communicate
“you haven’t picked a file”.</p>
<p>Now imagine the future where Web Components are supported
natively, and someone else is allowed to write a <code class="language-plaintext highlighter-rouge"><better-input></code>, an element
that is a real, encapsulated DOM element, and not just a div soup. Imagine using this <code class="language-plaintext highlighter-rouge"><better-input></code>
that isn’t implemented differently in each browser, that looks the same everywhere, and that
probably also knows how to bake you a cherry pie. IMAGINE. ✨</p>
Styling the Shadow DOM or: a metaphor gone too far2015-09-29T00:00:00+00:00https://meowni.ca/posts/styling-the-dome<p>One of the beefs (and there aren’t many) that I have with CSS is that it has a very weak
opinion about style encapsulation. That opinion is basically “well, name your classes well” or else bad
things happen. Know this: I come from C++, land of rules and disappointed compilers; this hand waviness drives me crazy.</p>
<p>This matters because now you have to trust the people that write your css libraries
to have common sense. If my website needs two kinds of fancy buttons, which live in <code class="language-plaintext highlighter-rouge">shiny-button.css</code> and <code class="language-plaintext highlighter-rouge">bouncy-button.css</code>, which are both libraries
written by silly people who want me to use the <code class="language-plaintext highlighter-rouge">.button</code> class to get their style,
I’m hosed.</p>
<h2 id="enter-the-shadow-dom">Enter the Shadow DOM</h2>
<p>The <a href="http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom-201/">Shadow DOM</a> fixes this problem by building a little castle (a dome, get it?) around each custom element, locking in its implementation and styles. This is a proper castle, with a proper moat, so now styles can’t get in and out of it. This means that if <code class="language-plaintext highlighter-rouge"><shiny-button></code> was a custom element instead of a pile of CSS, its <code class="language-plaintext highlighter-rouge">.button</code> class was scoped to the element itself, and wouldn’t stomp over <code class="language-plaintext highlighter-rouge"><bouncy-button></code>’s similarly creatively named <code class="language-plaintext highlighter-rouge">.button</code> class.</p>
<p>This shouldn’t surprise you too much, as native elements have been doing this in secret for yeaaaaars. <code class="language-plaintext highlighter-rouge"><input type=date></code> styles the date picker somehow, but you’ve never worried what class names it might use to do so. You know why? Because you can’t get to its castle, that’s why.</p>
<h2 id="the-struggle-is-real">The struggle is real</h2>
<p>So what happens if you <em>do</em> want to style <code class="language-plaintext highlighter-rouge"><shiny-button></code>? What if it’s a perfectly
respectable button, but it uses Helvetica as its font and you really need it to be Comic Sans because Helvetica is <em>so</em> 2014?</p>
<p>You can always style the <em>host</em> of the element. Think of the host as the castle walls; it’s the thing that holds all the actual contents of the custom element. It still plays by CSS rules, so some of the styles you set on the <em>host</em> could
actually trickle down to some child elements. For example:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">shiny-button</span> <span class="p">{</span>
<span class="nl">color</span><span class="p">:</span> <span class="no">white</span><span class="p">;</span>
<span class="nl">background-color</span><span class="p">:</span> <span class="no">tomato</span><span class="p">;</span>
<span class="nl">border-radius</span><span class="p">:</span> <span class="m">3px</span><span class="p">;</span>
<span class="nl">width</span><span class="p">:</span> <span class="m">400px</span><span class="p">;</span>
<span class="c">/* this will apply to any text in the button,
* unless a specific child overrides it */</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">14px</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>What you don’t get to do is peek at the implementation of the <code class="language-plaintext highlighter-rouge"><shiny-button></code> and decide you don’t need one of the nested
<code class="language-plaintext highlighter-rouge">div</code>s it uses. Again, these are the same rules that <code class="language-plaintext highlighter-rouge"><input type=date></code> plays by: you can change the <code class="language-plaintext highlighter-rouge">input</code>’s text to be red, but that date picker is what it is (hella ugly).</p>
<p>When the Shadow DOM was first introduced, people anticipated this styling problem and took the “bring an AK-47 to a knife fight” approach by giving every developer <a href="http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom-201/#toc-style-cat-hat">dragons</a>. These dragons are called <code class="language-plaintext highlighter-rouge">/deep/</code> and <code class="language-plaintext highlighter-rouge">::shadow</code>, and let you cross the moat and tear the shit out of any castle. You
could style anything you wanted in your custom element, because ain’t nobody stopping
dragons. It’s like that moat isn’t even there:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">shiny-button</span> <span class="o">/</span><span class="nt">deep</span><span class="o">/</span> <span class="nt">fancy-div</span><span class="nc">.fancy-class</span> <span class="o">></span> <span class="nc">.button</span> <span class="p">{</span>
<span class="nl">color</span><span class="p">:</span> <span class="no">red</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>However, as we know from Game of Thrones, you eventually discover that if you have a dragon, it’s going to start eating all your goats and people will regret giving you a dragon.</p>
<p>So we deprecated <code class="language-plaintext highlighter-rouge">/deep/</code> and <code class="language-plaintext highlighter-rouge">::shadow</code> and web developers around the world panicked.</p>
<h2 id="bridges-instead-of-dragons">Bridges instead of dragons</h2>
<p>The correct answer to “say, how do I cross this moat?” isn’t “lol a dragon”.
It’s a bridge. We’ve been using bridges to cross waters for like 3000 years. Dragons aren’t even real, man.</p>
<p>CSS variables (aka custom properties) do exactly that. They’re hooks that the developer of a <code class="language-plaintext highlighter-rouge"><shiny-button></code> has left all over the code,
so that you can change that particular style. Now you, as the user of a custom element no
longer need to know <em>how</em> that element is implemented. You are given the list of things you can style, and you’re set.</p>
<p>The code examples use Polymer, which is what I work on, and what I use to write custom elements. The full code, if you want to play along, is <a href="http://jsbin.com/qubila/edit?html,output">here</a> (there’s an embedded JSBin at the bottom of this post, but you know, spoilers).</p>
<h2 id="first-a-shiny-button">First, a shiny button</h2>
<p>So, here’s our button. It has a bunch of nested silly things, because why not. Who knows how the native <code class="language-plaintext highlighter-rouge"><input></code> actually looks like. Maybe it’s <code class="language-plaintext highlighter-rouge">divs</code> all the way down. Maybe it’s spiders. It’s probably spiders.</p>
<p>Everything inside <code class="language-plaintext highlighter-rouge">.container</code>, including <code class="language-plaintext highlighter-rouge">.container</code> itself is inside the Shadow Castle, so it can’t be reached:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><dom-module</span> <span class="na">id=</span><span class="s">"shiny-button"</span><span class="nt">></span>
<span class="nt"><template></span>
<span class="nt"><style></span>
<span class="nd">:host</span> <span class="p">{</span> <span class="nl">display</span><span class="p">:</span> <span class="n">inline-block</span><span class="p">;</span> <span class="nl">color</span><span class="p">:</span> <span class="no">white</span><span class="p">;}</span>
<span class="nc">.container</span> <span class="p">{</span> <span class="nl">background-color</span><span class="p">:</span> <span class="no">cornflowerblue</span><span class="p">;</span> <span class="nl">border-radius</span><span class="p">:</span> <span class="m">10px</span><span class="p">;</span> <span class="p">}</span>
<span class="nc">.icon</span> <span class="p">{</span> <span class="nl">font-size</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span> <span class="p">}</span>
<span class="nc">.text-in-the-shadow-dom</span> <span class="p">{</span> <span class="nl">font-weight</span><span class="p">:</span> <span class="m">900</span><span class="p">;</span> <span class="p">}</span>
<span class="nt"></style></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">></span>
<span class="nt"><span</span> <span class="na">class=</span><span class="s">"icon"</span><span class="nt">></span>♡<span class="nt"></span></span>
<span class="nt"><span</span> <span class="na">class=</span><span class="s">"user-text"</span><span class="nt">><content></content></span></span>
<span class="nt"><span</span> <span class="na">class=</span><span class="s">"text-in-the-shadow-dom"</span><span class="nt">></span>!!!<span class="nt"></span></span>
<span class="nt"></div></span>
<span class="nt"></template></span>
<span class="nt"><script></span>
<span class="nx">Polymer</span><span class="p">({</span> <span class="na">is</span><span class="p">:</span> <span class="dl">'</span><span class="s1">shiny-button</span><span class="dl">'</span> <span class="p">});</span>
<span class="nt"></script></span>
<span class="nt"></dom-module></span>
...
<span class="c"><!-- somewhere in an index.html, you'd use it like so: --></span>
<span class="nt"><shiny-button></span>hallo hai<span class="nt"></shiny-button></span>
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge"><shiny-button</code>> looks like the thing on the left. Pretty meh. We’ll do better. We’ll style it
to be the thing on the right, without any 🐲🐲🐲.</p>
<p><img width="312" alt="screen shot 2015-08-11 at 3 34 51 pm" src="https://cloud.githubusercontent.com/assets/1369170/9212530/97d07e7c-403e-11e5-867e-656ee1fd3cb7.png" /></p>
<h2 id="what-can-you-style-right-now">What can you style right now?</h2>
<p>We can only style the <em>host</em> of the element – this is everything outside the <code class="language-plaintext highlighter-rouge">.container</code> class, but inside
the shiny button. You know, the walls of the castle.</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">shiny-button</span><span class="nc">.fancy</span> <span class="p">{</span>
<span class="nl">font-family</span><span class="p">:</span> <span class="s1">"Lato"</span><span class="p">;</span>
<span class="nl">font-weight</span><span class="p">:</span> <span class="m">300</span><span class="p">;</span>
<span class="nl">color</span><span class="p">:</span> <span class="no">black</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>To see the difference between the host and the container, we can give the button itself a different
background than the <code class="language-plaintext highlighter-rouge">.container</code>. The red corners you see are part of the host; the blue parts are
the <code class="language-plaintext highlighter-rouge">.container</code>.</p>
<p><img width="142" alt="screen shot 2015-08-11 at 3 23 20 pm" src="https://cloud.githubusercontent.com/assets/1369170/9212326/ed035506-403c-11e5-848a-9b35bbdc8fce.png" /></p>
<p>Of course, none of these styles will work, because these <code class="language-plaintext highlighter-rouge">divs</code> are well inside the castle:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">shiny-button</span><span class="nc">.fancy</span> <span class="nc">.container</span> <span class="p">{</span>
<span class="nl">color</span><span class="p">:</span> <span class="no">red</span><span class="p">;</span>
<span class="nl">background-color</span><span class="p">:</span> <span class="no">pink</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">shiny-button</span><span class="nc">.fancy</span> <span class="nc">.text-in-the-shadow-dom</span> <span class="p">{</span>
<span class="nl">font-weight</span><span class="p">:</span> <span class="m">300</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="and-now-some-bridges">And now: some bridges</h2>
<p>We probably want to change the button’s background color, so we’ll create a variable for it, called <code class="language-plaintext highlighter-rouge">--shiny-button-background</code>. Some things:</p>
<ul>
<li>every Polymer custom property needs to start with a <code class="language-plaintext highlighter-rouge">--</code>, so that Polymer knows you’re not just typing gibberish.</li>
<li>I like to include the element name as a prefix to the custom property; I find it useful to remind me what I’m actually styling.</li>
<li>I also like documenting these somewhere in a giant docs blurb, so that the element’s users know what to expect. Polymer’s <a href="https://github.com/PolymerElements/paper-checkbox/blob/master/paper-checkbox.html#L34">paper-checkbox</a> is a nice example of this (because I wrote it, obvs).</li>
</ul>
<p>Now that we know a custom property is available, this is how we would use it, inside the custom element:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.container</span> <span class="p">{</span>
<span class="c">/* cornflowerblue is a default colour, in case the user doesn't
* provide one. You could omit it if it's being inherited from above */</span>
<span class="nl">background-color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--shiny-button-background</span><span class="p">,</span> <span class="no">cornflowerblue</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>You can think of <code class="language-plaintext highlighter-rouge">var</code> like an eval, which says “apply the value of this custom property, whatever that value is”. And this is how you, the user of the element would actually give it a value:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">shiny-button</span><span class="nc">.fancy</span> <span class="p">{</span>
<span class="c">/* see how much this looks like a normal css property? i.e.
background: #E91E63; */</span>
<span class="py">--shiny-button-background</span><span class="p">:</span> <span class="m">#E91E63</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p><br /></p>
<p>You can add all sorts of hooks for these kinds of “one-off” custom properties. Eventually you will realize that if the thing that should be styled is too generic (the background container of the button) there’s waaaaay too many CSS properties to expose one by one. In that case, you can use a <em>mixin</em>, which is like a bag of properties that should all be applied at once. By default this bag is empty, so nothing gets applied when defining the custom element:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.icon</span> <span class="p">{</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span>
<span class="err">@apply(--shiny-button-icon);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>But the user of the element could start adding things to the bag like this:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">shiny-button</span><span class="nc">.fancy</span> <span class="p">{</span>
<span class="nl">font-family</span><span class="p">:</span> <span class="s1">"Lato"</span><span class="p">;</span>
<span class="nl">font-weight</span><span class="p">:</span> <span class="m">300</span><span class="p">;</span>
<span class="nl">color</span><span class="p">:</span> <span class="no">black</span><span class="p">;</span>
<span class="py">--shiny-button-background</span><span class="p">:</span> <span class="m">#E91E63</span><span class="p">;</span>
<span class="c">/* this is the mixin! the colon and the semicolon are both important */</span>
<span class="py">--shiny-button-icon</span><span class="p">:</span> <span class="err">{</span>
<span class="n">color</span><span class="p">:</span> <span class="no">red</span><span class="p">;</span>
<span class="nl">padding</span><span class="p">:</span> <span class="m">10px</span><span class="p">;</span>
<span class="nl">text-shadow</span><span class="p">:</span> <span class="m">0</span> <span class="m">1px</span> <span class="m">1px</span> <span class="m">#880E4F</span><span class="p">;</span>
<span class="p">}</span><span class="o">;</span>
<span class="err">}</span>
</code></pre></div></div>
<p><br />
Some tips:</p>
<ul>
<li>the mixin is only relevant to the selector it’s being applied to (modulo CSS inheritance rules). As an element author
it’s your responsability to name this mixin in a way that conveys this. In the example above, <code class="language-plaintext highlighter-rouge">--shiny-button-icon</code>
implies you’re styling the icon of the button. If instead you’re applying that style to the text, for example,
you’re being a bad element author, and your users will shame you on social media.</li>
<li>mixins aren’t a panacea. If you look at the <a href="https://github.com/PolymerElements/paper-checkbox/blob/master/paper-checkbox.html#L34">paper-checkbox</a>
example I mentioned before, you’ll notice no mixins at all! This is because the element is fairly restricting, and
there’s only so many things you can possibly care about styling. That’s when I tend to prefer individual custom properties vs a mixin.</li>
</ul>
<p>That’s it, that’s all! We can style ALL the things now, AND get style encapsulation,
and not sacrifice any goats to dragons. Aren’t web components amazing? (Yes they are).</p>
<p><br /></p>
<p>Here’s the JSBin if you want to play with it:
<a class="jsbin-embed" href="http://jsbin.com/qubila/embed?html,output">JS Bin on jsbin.com</a><script src="http://static.jsbin.com/js/embed.min.js?3.34.2"></script></p>
<h1 id="hear-me-talk-about-this">Hear me talk about this</h1>
<p>I gave this talk at the Polymer <a href="https://www.polymer-project.org/summit">summit</a>. Hurray, the metaphor is spreading!</p>
<h2 id="video">Video</h2>
<iframe width="560" height="315" src="https://www.youtube.com/embed/IbOaJwqLgog" frameborder="0" allowfullscreen=""></iframe>
<h2 id="slides">Slides</h2>
<script async="" class="speakerdeck-embed" data-id="9c0e7a1b528a4293b63e09f1e3c04044" data-ratio="1.77777777777778" src="//speakerdeck.com/assets/embed.js"></script>
Keypress is bananas2015-06-02T00:00:00+00:00https://meowni.ca/posts/keypress-is-bananas<p>The <code class="language-plaintext highlighter-rouge">keypress</code> event works maddeningly differently in Chrome/Safari and Firefox, and this is the story of how I spent two hours discovering that, so that hopefully you don’t have to.</p>
<h2 id="keypress-what">Keypress what?</h2>
<p>A <code class="language-plaintext highlighter-rouge">keypress</code> event is one of the events you get when you mash on the keyboard. It’s special because according to the <a href="https://developer.mozilla.org/en-US/docs/Web/Events/keypress">spec</a>, you should only get a <code class="language-plaintext highlighter-rouge">keypress</code> event for keystrokes that produce printable characters. So you’ll get it for things like letters and symbols, but not for <code class="language-plaintext highlighter-rouge">backspace</code> and <code class="language-plaintext highlighter-rouge">left arrow</code>.</p>
<p>It’s a great event to have if you want to write some as-you-type validation on an input, and you want to be able to dismiss the non-printable characters (which will still generate key events, but are uninteresting to the validation bit).</p>
<p>Chrome, Safari and IE10 agree with this interpretation, which is great news.</p>
<p>To be contrarian, Firefox (38; I don’t know about Aurora) <em>always</em> sends a <code class="language-plaintext highlighter-rouge">keypress</code> event for anything you type. It’s basically a <code class="language-plaintext highlighter-rouge">keydown</code> event from what I see. Now you have to get rid of control characters yourself and you get write code that doesn’t make sense on the other platforms! Yay! (not yay)</p>
<p>I call shenanigans.</p>
<h2 id="mind-your-keycodes-and-charcodes">Mind your keyCodes and charCodes</h2>
<p>From looking at the <a href="https://developer.mozilla.org/en-US/docs/Web/Events/keypress">spec</a>, we expect a <code class="language-plaintext highlighter-rouge">keypress</code> event to have:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">keyCode</code>, a number code that represents the key you’ve pressed. For example, <code class="language-plaintext highlighter-rouge">q</code> is <code class="language-plaintext highlighter-rouge">113</code>. This is allegedly deprecated, but don’t worry, both Firefox and Chrome implement it, but differently.</li>
<li><code class="language-plaintext highlighter-rouge">charCode</code>, the unicode number of the key. This code only exists for <code class="language-plaintext highlighter-rouge">keypress</code>. Like before, it’s deprecated, but like before, it’s implemented by both browsers. Differently.</li>
<li><code class="language-plaintext highlighter-rouge">key</code>, the value of the key represented by the event. According to that spec, this one is unimplemented. Worry not, Firefox implements it just fine (Chrome doesn’t). It is the hero we deserve, but not the one we get right now.</li>
</ul>
<h2>😭</h2>
<p>What you get out of this is spectacularly annoying. I wrote some <a href="http://output.jsbin.com/tuboguyido/1/">code</a> that basically prints out what the <code class="language-plaintext highlighter-rouge">keypress</code> event looks like, and found:</p>
<ul>
<li>As promised, in Chrome/Safari/IE10, we only get the <code class="language-plaintext highlighter-rouge">keypress</code> event for printable things. The event always has the same values for <code class="language-plaintext highlighter-rouge">keyCode</code> and <code class="language-plaintext highlighter-rouge">charCode</code>, and doesn’t have a <code class="language-plaintext highlighter-rouge">key</code>.</li>
</ul>
<p><img src="/images/keypress/chrome.gif" alt="Chrome keypress events" /></p>
<ul>
<li>In Firefox, you get the <code class="language-plaintext highlighter-rouge">keypress</code> event for ALL the things, BUT:
<ul>
<li>for printable characters, <code class="language-plaintext highlighter-rouge">keyCode = 0</code>, and <code class="language-plaintext highlighter-rouge">charCode</code> has a sane value.</li>
<li>for control characters, <code class="language-plaintext highlighter-rouge">charCode = 0</code>, and <code class="language-plaintext highlighter-rouge">keyCode</code> has a sane value.</li>
<li>this is super because if you’ve been testing on the other browsers and have been using <code class="language-plaintext highlighter-rouge">String.fromCharCode()</code>, you’re going to get hilariously bad results if you use the wrong code. Like how <code class="language-plaintext highlighter-rouge">arrow left</code> could actually be <code class="language-plaintext highlighter-rouge">%</code>.</li>
<li>see how <code class="language-plaintext highlighter-rouge">key</code> is kind of nice though? One day, at a browser near you.</li>
</ul>
</li>
</ul>
<p><img src="/images/keypress/firefox.gif" alt="Chrome keypress events" /></p>
<p>There, now you know. And knowing is half the battle.</p>
<h2 id="ps">P.S.</h2>
<p>If you read this in the future and the future doesn’t work like I said it does, either I was wrong (highly likely), or someone fixed something. Let me know and I can make updates.</p>
Why Chromium has code owners2015-03-24T00:00:00+00:00https://meowni.ca/posts/chromium-owners<p>My favourite thing about the Chromium code is this <a href="https://chromium.googlesource.com/chromium/src/+/d413b2dcb54d523811d386f1ff4084f677a6d089/chrome/browser/chrome_browser_main_mac.mm#37">enum</a> of cats and all the comments in that file. My second favourite thing is <code class="language-plaintext highlighter-rouge">OWNER</code> files. Guess what this post is about (hint: it’s not about cats NOT EVERYTHING IS ABOUT CATS, OK?)</p>
<p>Edit: In a clear and deliberate conspiracy, the cats have been removed from Chromium. The old new cool thing is <a href="https://code.google.com/p/chromium/codesearch#chromium/src/base/pickle.h&q=pickle&sq=package:chromium&type=cs&l=138">pickles</a>, and the new new cool thing is <a href="https://code.google.com/p/chromium/codesearch#chromium/src/base/mac/objc_property_releaser_unittest.mm&l=16">Count Von Counts</a>. Bonus points to @thakis for finding that last one. 💁</p>
<h2 id="why-should-you-care">Why should you care?</h2>
<p>Owners in Chromium are people who own an area of code. This can be a small feature (the <code class="language-plaintext highlighter-rouge">chrome://settings</code> page) or a giant area (all of the Cocoa UI). You don’t <em>have</em> to be an owner to be successful – you get to be an owner because you <em>want</em> to. This usually means that you have contributed a lot to that particular nugget of code, have acquired a slightly unhealthy obsession for it (symptoms: if you’ve whispered “my precious” to a line of code in the last hour, you will make a great code owner one day), and generally care about its well being. I have been trying (unsuccessfully) for years to be an owner of pizza; hit me up if you have any leads.</p>
<p>Owners are gatekeepers of code, and their main responsibility is making sure the code doesn’t go to shit. Comments that make sense. No copy pasting, no hacks, no soup for you. None of that “I don’t really know how to make this code better so I’m going to merge it and run” nonsense. They are the very model of a modern Major-General, they know the kings of England, and they quote the fights historical.</p>
<p>TL; DR: owners won’t let you merge crappy code. Imagine if each of the 2000 Chromium commiters merged a random hack in one of the 7 million lines of code we have. IMAGINE. 🔥🔥🔥</p>
<h2 id="what-it-means-for-owners">What it means for owners</h2>
<p>Realtalk: being an owner means that people will send you a lot of code to review, because your blessing (or “LGTM”) is required for that code to be committed. <a href="https://codereview.chromium.org/search?closed=1&owner=&reviewer=sky%40chromium.org&cc=&repo_guid=&base=&project=&private=1&commit=1&created_before=&created_after=&modified_before=&modified_after=&order=&format=html&keys_only=False&with_messages=False&cursor=&limit=30">@sky</a> is an owner of the Windows UI code, and he does something like 500+ reviews a quarter. And also writes code. And helps me out when I (invariably) break the UI. He’s pretty much the best.</p>
<p>Basically:</p>
<ul>
<li>People will ask you general questions when they’re stuck. It’s totally fine not to know the answer – you’ll probably at least know who to point them at.</li>
<li>Whenever shit hits the fan and it’s on your turf of code, if no obvious culprit is to be found, you win the lottery and get to fix it. Spoilers: this sometimes means fixing things that you didn’t actually break. Currently, I’m on day 6 of this giant yak shave that I won by fixing a random crash. Regrets, I am them.</li>
<li>You get to live the dream and be picky about code. Don’t like a method’s name? A particular comment? Think that there’s a bit of a refactor needed to make this better? You get to ask for it, and guess what: people usually have to listen.</li>
</ul>
<p>👉 Developers trust owners to not be insane. Owners trust developers not to try to commit stuff behind their back. This is why it works. 👈</p>
<h2 id="what-it-means-for-developers">What it means for developers</h2>
<p>First, when you’re stuck, you know who to ask questions (an owner!). Second, in order for you to commit any code, you need to get the owners’ approval for your changes.</p>
<p>Here’s an <a href="https://codereview.chromium.org/861053004">example</a> of a code review. I like to explicitly <a href="https://codereview.chromium.org/861053004/#msg11">mention</a> which owner should review which file, because one person might own multiple files/areas in a given CL (if you’re a <code class="language-plaintext highlighter-rouge">chrome/browser</code> owner, you own ALL of the things), but might not be required to review all of them.</p>
<p>So, who owns <code class="language-plaintext highlighter-rouge">profile_info_cache.cc</code>? Everyone named in the <code class="language-plaintext highlighter-rouge">chrome/browser/profiles/OWNERS</code> file. On top of that, everyone up the directory tree (so in <code class="language-plaintext highlighter-rouge">chrome/browser/OWNERS</code>) is also an owner. If you stumble on a directory that doesn’t have an owners file (for example <code class="language-plaintext highlighter-rouge">chrome/browser/ui/cocoa/profiles</code>), just crawl on up until you find the closest one (in this case, you would add an owner from <code class="language-plaintext highlighter-rouge">chrome/browser/ui/cocoa/OWNERS</code>. This is also useful if you do a fairly innocent refactor that touches a lot of files, like renaming a method. In that case, rather than adding 17 different owners, you can just get one, root owner and run with that.</p>
<h2 id="how-you-can-get-owner-files-in-your-project">How YOU can get owner files in your project</h2>
<p>If you want to implement owner files for your projects (YAY!), you need to do a couple of things:</p>
<ul>
<li>Add some sort of presubmit check so that people can’t commit code without getting all their ducks in a row. If you give people a chance to merge code under the radar, they will. So, don’t.</li>
<li>Here’s the Chromium <a href="https://code.google.com/p/chromium/codesearch#chromium/src/PRESUBMIT.py&l=996">script</a>. It will probably most likely not work out of the box, but it could be a useful starting point.</li>
<li>Create OWNER files in all the directories that makes sense. Format them in a way that scripts can read them. Here are <a href="https://code.google.com/p/chromium/codesearch#search/&q=OWNERS&type=cs&sq=package:chromium">all</a> the Chromium ones.</li>
<li>Owner files can have rules <a href="https://code.google.com/p/chromium/codesearch#chromium/src/chrome/browser/profiles/OWNERS&l=1">per subdirectory</a> but also <a href="https://code.google.com/p/chromium/codesearch#chromium/src/chrome/browser/profiles/OWNERS&l=15">per file</a>. For really tedious file changes (like build files), any committer can be an owner using <a href="https://code.google.com/p/chromium/codesearch#chromium/src/chrome/common/OWNERS&l=4">wildcards</a>).</li>
<li>Make sure the owner files are up to date: when people leave teams, remove them. When people start becoming friendly with an area of code, let them know that ownership is an option.</li>
<li>Watch how your code gets better over time.</li>
</ul>
Contributing to Chromium: an illustrated guide2015-02-10T00:00:00+00:00https://meowni.ca/posts/chromium-101<p>I gave a talk about how to get started contributing to Chromium, but it wasn’t recorded,
and my <a href="https://speakerdeck.com/notwaldorf/contributing-to-chromium">slides</a> by themselves look like cold-medicine induced hallucinations
(which, to be fair, they were). So instead, here is a giant blog post that will take you
through every step from “checking out the code” to “landing the code in the Chromium repo”. It will also come in super handy for mild to moderate cases of insomnia.</p>
<p>If you just want a TL;DR or a refresher of the commands you might need, check out the <a href="https://speakerdeck.com/notwaldorf/contributing-to-chromium">slides</a>. They’re basically bullet points without the running commentary.</p>
<p><strong>Warning</strong>: this is a long post. The bug we’re fixing is silly, but will get
us writing actual Chromium code. If you want a
real good-first-bug to fix after this, <a href="https://code.google.com/p/chromium/issues/list?q=Hotlist:GoodFirstBug">here</a> is a nice list. Usually unassigned bugs (with no owner) are free for the taking, but it can also happen that a bug will be assigned to a human who is not actually working on it. Check the activity on it – if there haven’t been any activities in a while, leave a message on the bug or ping the owner and tell them you’d like to work on it!</p>
<h2 id="get-your-computer-ready">Get your computer ready</h2>
<p>Chrome is giant. It needs a beefy machine (we recommend a 64-bit OS, with at least 8GB of RAM. A separate SSD to hold/build your code will make your life infinitely more pleasant), and a couple dozen goat sacrifices. Even then, building Chromium from scratch is slow. Snails run the half mile faster (fact). This is something you might as well get used to.</p>
<p>We have a pretty solid set of <a href="http://www.chromium.org/developers/how-tos/get-the-code">instructions</a> on
how to get everything set up. I promise you this page has been used and reviewed a billion times, it’s up to date,
and every step in it is important. Don’t skip steps because you think you don’t need them. You do.</p>
<p>However, I’ll tell you about the custom <a href="https://github.com/notwaldorf/.not-quite-dotfiles/blob/master/zsh/chrome">things</a> that I use that <em>aren’t</em> on that page, which I’m pretty proud of.</p>
<ul>
<li>I always build <code class="language-plaintext highlighter-rouge">Release</code> builds, because they’re faster. This means I don’t get as many debug symbols as I would like. That’s fine for me, because I’m a chicken
and pretty scared of <code class="language-plaintext highlighter-rouge">lldb</code>, and I debug with <code class="language-plaintext highlighter-rouge">printfs</code> like this is the 80s anyway</li>
<li><del>I’ve added <code class="language-plaintext highlighter-rouge">component=shared_library</code> (so that incremental builds are super fast) and <code class="language-plaintext highlighter-rouge">dcheck_always_on=1</code> (so that even though I have
a <code class="language-plaintext highlighter-rouge">Release</code> build, debug asserts still get hit) to my <code class="language-plaintext highlighter-rouge">GYP_DEFINES</code>. <a href="http://www.chromium.org/developers/gyp-environment-variables">Here</a> are all the ways in which you can set up your <code class="language-plaintext highlighter-rouge">GYP_DEFINES</code>. I use the <code class="language-plaintext highlighter-rouge">chromium.gyp_env</code> way on Windows (because I don’t understand <code class="language-plaintext highlighter-rouge">PATH</code> variables) and the <code class="language-plaintext highlighter-rouge">Environment Variable</code> way on Mac/Linux, because I sort of understand <code class="language-plaintext highlighter-rouge">exports</code>. Realtalk, I really don’t know any Windows, and I’m ok with that</del>. Chromium switched to <code class="language-plaintext highlighter-rouge">gn</code>; check <a href="https://chromium.googlesource.com/chromium/src/+/master/docs/mac_build_instructions.md#Faster-builds">this</a> out for how to enable the component build with <code class="language-plaintext highlighter-rouge">gn</code>. The bit about me not knowing any Windows is still true, though.</li>
<li>I have a fancy set of <a href="https://github.com/notwaldorf/.not-quite-dotfiles/blob/master/zsh/chrome#L15">aliases</a> like <code class="language-plaintext highlighter-rouge">make_</code> and <code class="language-plaintext highlighter-rouge">go_</code>, so that I don’t have to
remember about which flags I want to run Chrome with. They come in <code class="language-plaintext highlighter-rouge">doskey</code> variants on Windows</li>
<li>Don’t use cygwin on Windows. It doesn’t play nice with the <code class="language-plaintext highlighter-rouge">depot_tools</code></li>
<li>I use Atom, and have used Sublime as an editor. Last time I checked, XCode beach balled, huffing and puffing, when trying to load the code. Visual Studio works pretty well if you can stand Windows and its insane command prompt. You can use ctags if you want; I don’t. I use a dumb editor, and find code through
<code class="language-plaintext highlighter-rouge">git grep</code> and the Chromium <a href="https://code.google.com/p/chromium/codesearch">codesearch</a>, because I’m metal like that. You can use anything you want. Literally nobody cares.</li>
</ul>
<p><strong>Edit</strong> (Aug 9, 2016): Chromium just switched to <code class="language-plaintext highlighter-rouge">gn</code>, which means some of the comments below don’t apply. Check these <a href="https://chromium.googlesource.com/chromium/src/+/master/docs/mac_build_instructions.md">mac build instructions</a> for how to make your build fast again, using <code class="language-plaintext highlighter-rouge">gn</code>.
<strong>Edit</strong> (Feb 17, 2016): I wrote a detailed <a href="https://github.com/notwaldorf/ama/issues/34#issuecomment-185038702">answer</a> that contains a whole bunch of extra tricks to make builds faster.</p>
<h2 id="get-your-body-ready">Get your body ready</h2>
<p>Chromium has a <a href="http://www.chromium.org/developers/coding-style">code style</a>. Do
not panic if your first review will have 20 comments that are code style nits. It’s
absolutely normal, and nobody thinks less of you. On the contrary, we try to be
extra picky with new people, so that they learn everything as quickly as possible.</p>
<p>Chromium is hard. I’ve been working on it for two years, and it’s still hard.
There’s a loooot of code, and you’ll spend a fair bit of time looking for the right
bit of code you care about. Don’t be afraid to ask questions if you’re stuck. It took me <em>forever</em> not to be scared of asking questions, but it turns out all the
people that told me that everyone is nice and helpful were right: everyone IS nice
and helpful, because at some point they were you, the code was as scary then as it is now, and the compiler has never stopped barfing errors since the day it was born.</p>
<ul>
<li>IRC: there’s a <a href="http://echelog.com/logs/browse/chromium">#chromium</a> room for dev-related questions. It’s a bit of a zombieland outside of PST hours</li>
<li>mailing list: <a href="https://groups.google.com/a/chromium.org/forum/#!forum/chromium-dev">chromium-dev@google.com</a>. I strongly recommend to search the archives before you ask a new question. A lot of common things have been asked, and people tend to get a bit grumpy if you ask “how do I get infinite quota translate API keys” for literally the thousandth time</li>
<li>if you’re still stuck and panicked, email <a href="mailto:noms@chromium.org">me</a>. I might not know the answer, and I might be super busy, but I promise to be nice and help in whichever way I can. Gifs of animals doing silly things are encouraged</li>
</ul>
<h2 id="omg-lets-write-some-code">OMG let’s write some code!</h2>
<p>We’re going to add a button to Chrome. It’s going to be the best thing ever (second to a freshly opened can of Pringles). For realsies. It will be in this bubble here, and it will open [redacted] (2019 update: I let that URL expire and now it apparently points to nsfw things so uhhhh don’t click it.) in a new tab. This is the before and after:</p>
<p><img tabindex="0" width="656" src="/images/chromium/before_after.png" alt="The before image has 2 buttons in the Chrome profile menu, 'switch person' and 'go incognito'. The after image has a third button, 'Can I have a pony?', with a star icon, that has been inserted above the other 2 buttons." /></p>
<p>(side note: if you don’t see that button in your dev build of Chromium, launch it with <code class="language-plaintext highlighter-rouge">--enable-new-avatar-menu</code>. The UI is enabled by default on all of the released Chromes through a server side flag, but that bit of magic doesn’t run on dev builds, so you need to turn it on yourself)</p>
<p>I chose this dialog because the easiest way to find your way through code is for there to be a searchable string in there like “Switch Person”. Also I wrote this bubble, so it’s Pretty Clutch™.</p>
<h4 id="0-make-a-branch">0. Make a branch</h4>
<p>First things first: <em>always</em> create a new branch for every bug/feature/bit of code you’re working on. Working directly
on the master branch is bad news bears: 1) it’s very unlikely you’re working on one thing at a time, 2) pulling new code from the remote master to your local repo becomes an adventure. .<strong>TL; DR</strong>: don’t work on master evar. So,</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(´ ▽`).。o♡ src on master ❥ git checkout -b add-pony-button origin/master
Branch add-pony-button set up to track remote branch master from origin by rebasing.
Switched to a new branch 'add-pony-button'
(´ ▽`).。o♡ src on add-pony-button ❥
</code></pre></div></div>
<h4 id="1-find-the-code">1. Find the code</h4>
<p>Hooman, meet <a href="https://code.google.com/p/chromium/codesearch">codesearch</a>. It’s your best friend in Chromium. It knows where all the codes are and who they’re called by, and where interfaces get implemented. I spend so much time with it, I’ll probably send it a Valentine’s Day card this year. Anyway, search for “Switch Person” in there, and get these results</p>
<p><a href="https://code.google.com/p/chromium/codesearch#search/&q=%22switch%20person%22&sq=package:chromium&type=cs"><img width="656" src="/images/chromium/codesearch_1.png" alt="The search gives two results, described below" /></a></p>
<p>First, <code class="language-plaintext highlighter-rouge">generated_resources.grd</code> is where most of the
strings in Chrome live. A giant file makes
internationalization sooper easy – you hand out the file to translators, they
give you back the same file in a different language, and at startup, Chrome
decides which file to load based on its locale. Bingo bango, localized UI.</p>
<p>Some of the results have <code class="language-plaintext highlighter-rouge">ACCESSIBLE_NAME</code> in them, which means that they’re accessibility strings (hint: they’re read out loud by VoiceOver apps). <code class="language-plaintext highlighter-rouge">IDS_PROFILES_SWITCH_USERS_BUTTON</code> looks promising though, so let’s see where it’s used.</p>
<p><a href="https://code.google.com/p/chromium/codesearch#search/&q=IDS_PROFILES_SWITCH_USERS_BUTTON&sq=package:chromium&type=cs"><img width="656" src="/images/chromium/codesearch_2.png" alt="The files that are relevant and appear in the search are 'profile_chooser_view.cc' and 'profile_chooser_controller.mm'" /></a></p>
<p>Aside from the <code class="language-plaintext highlighter-rouge">generated_resources.grd</code> results from before, we have <em>two</em> new files!</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">chrome/browser/ui/views/profiles/profile_chooser_view.cc</code> – This is in a <code class="language-plaintext highlighter-rouge">ui</code>
subfolder, which means it’s a UI related file (good sign), so probably a dialog or a bubble. On
top of that, it’s a <code class="language-plaintext highlighter-rouge">.cc</code> file in a <code class="language-plaintext highlighter-rouge">views</code> folder, which means it’s Windows/Linux code</li>
<li><code class="language-plaintext highlighter-rouge">chrome/browser/ui/cocoa/profiles/profile_chooser_controller.mm</code> – The <code class="language-plaintext highlighter-rouge">.mm</code> is
a dead give-away this is a Mac UI file. On OSX we write our UI in Objective-C and drink a lot of wine to forget</li>
</ul>
<p>I’m doing this demo on the Mac, so let’s look at <code class="language-plaintext highlighter-rouge">profile_chooser_controller.mm</code>. I’ve
written both of these files, so I promise you they’re SUPER similar.</p>
<h4 id="2-adding-a-button">2. Adding a button</h4>
<p>Ok, so now I’m looking at <code class="language-plaintext highlighter-rouge">profile_chooser_controller.mm</code> and here’s how my brain would start nomming this code: that string ID is used in a button that
lives in a method called <code class="language-plaintext highlighter-rouge">-createOptionsViewWithRect:</code>. This method is called by
<code class="language-plaintext highlighter-rouge">-buildProfileChooserView:</code>, which in turn is called by <code class="language-plaintext highlighter-rouge">-initMenuContentsWithView:</code>. You
can go down this rabbit hole for days, but the basic idea is that this is clearly the place where we draw buttons in this bubble.</p>
<p>If we look at <code class="language-plaintext highlighter-rouge">-createOptionsViewWithRect:</code> in particular, it
does the following:</p>
<ul>
<li>creates a drawing rectangle that’s of a fixed width and fixed height. This is the
size of each of those buttons. If you’re not familiar with Cocoa (who can blame you),
the way this works is that we draw everything in
relative coordinates. We’re basically going to keep this rectangle fixed, and just
change the <code class="language-plaintext highlighter-rouge">y</code> coordinate at which we’re drawing. Also: <code class="language-plaintext highlighter-rouge">y=0</code> is the bottom of the screen,
and <code class="language-plaintext highlighter-rouge">y=a billion</code> is the top of the screen, and we always draw bottom to top.
Say it with me, “because Cocoa”.</li>
<li><code class="language-plaintext highlighter-rouge">-hoverButtonWithRect:</code> is a utility function that draws a fancy button with a text,
an image, and an action selector (that’s Cocoa-speak for “click handler”)</li>
<li>If we’re allowed to display the lock button, it creates and draws <code class="language-plaintext highlighter-rouge">lockButton</code>.
Spoilers: <code class="language-plaintext highlighter-rouge">displayLock</code> is <code class="language-plaintext highlighter-rouge">false</code> unless you do some Chrome gymnastics I honestly don’t recommend, because they’re way less fun than they sound</li>
<li>If we’re allowed to display the incognito button (we are), create and draw it</li>
<li>Finally, create and draw a button whose string is “Exit Guest” if we’re a Guest session, or “Switch person” otherwise</li>
<li>Did you see how we drew everything bottom to top? Yeah. That’s a thing.</li>
</ul>
<p>Hey! We should do the same thing! Let’s add our <code class="language-plaintext highlighter-rouge">ponyButton</code> right below the
<code class="language-plaintext highlighter-rouge">switchUsersButton</code> (which, again, means it’s being drawn above it ARE YOU HAVING FUN YET???). The highlighted bits are the new code.</p>
<p><a href="https://gist.github.com/notwaldorf/9dda1a00709cc24a5b02#file-1-creating-a-pony-button-mm"><img width="656" src="/images/chromium/code_button.png" alt="Code showing how to instantiate a pony button" /></a></p>
<p>The code we just wrote says that when you click on the <code class="language-plaintext highlighter-rouge">ponyButton</code>, we call a method called <code class="language-plaintext highlighter-rouge">-goPoniesGo:</code>. We should probably write it, so that we can actually test our code. It will only log something to the console for now, because logging code is the best code.</p>
<p><a href="https://gist.github.com/notwaldorf/9dda1a00709cc24a5b02#file-2-adding-the-go-ponies-go-handler-mm"><img width="656" src="/images/chromium/pony_code_1.png" alt="Code showing how to create the click handler, and add a logging statement to it" /></a></p>
<p>If you build and run this, your bubble should look like the “after” image described before,
and clicking the button should spew things on the console.</p>
<h4 id="3-making-the-button-go">3. Making the button go</h4>
<p>This bit is a leap of faith. We want to open a URL in a new tab, but we don’t
really know how. If you search for things like <code class="language-plaintext highlighter-rouge">open in new tab</code>, you can hope
to hit some comments, but <code class="language-plaintext highlighter-rouge">tabs</code> are kind of like the prom queen of the browser so you’re going to get a crap load of useless results. Unfortunately for us, I know that we’re looking for a method called <code class="language-plaintext highlighter-rouge">chrome::ShowSingletonTab</code> (in <code class="language-plaintext highlighter-rouge">chrome/browser/ui/singleton_tabs.cc</code>). Had I not known this, I think I would have found it, for example, by checking how the
“Settings” item in the hot dog menu (or hamburger menu, call it whatever food you wish) opens the “chrome://settings” tab. It will take some digging.</p>
<p>If you don’t know how to use <code class="language-plaintext highlighter-rouge">ShowSingletonTab()</code>, I would <code class="language-plaintext highlighter-rouge">codesearch</code> again for different
uses of the function. This time, just by looking at the method signature, we
can figure out we should write:</p>
<p><a href="https://gist.github.com/notwaldorf/9dda1a00709cc24a5b02#file-3-implementing-the-go-ponies-go-handler-mm"><img width="656" src="/images/chromium/pony_code_2.png" alt="Code showing how to add a line to the click handler to open a new tab" /></a></p>
<p>Because the stars aligned and Mercury wasn’t in retrograde, we had all
of the <code class="language-plaintext highlighter-rouge">.h</code> header files already included for this to work. Compile it, run it, and get a pony!</p>
<h2 id="send-your-code-for-review">Send your code for review</h2>
<p>I don’t know about you, but I’m preeeeeeetty proud of this feature, so I feel we’re ready to send it for review!
Run <code class="language-plaintext highlighter-rouge">git commit -am "added pony button"</code> to commit this file, and <code class="language-plaintext highlighter-rouge">git cl upload</code> to upload it to <a href="https://codereview.chromium.org/">codereview</a>.</p>
<p>In your <code class="language-plaintext highlighter-rouge">git cl upload</code> message, write a meaningful description, a crbug ID, and a blurb about how to
test this. This is what I would write:</p>
<p><a href="https://gist.github.com/notwaldorf/9dda1a00709cc24a5b02#file-4-sample-cl-description-text"><img width="656" src="/images/chromium/cl_description.png" alt="Sample CL description. It has a title which is less than 72 characters, a summary, a crbug id, and detailed testing instructions" /></a></p>
<p>And if that goes well, this is what your CL (stands for change-list. Comes from the dark days of Perforce) should look like on the site! (that CL doesn’t exist anymore, but here’s a <a href="https://codereview.chromium.org/1042923003/">random</a> CL as an example)</p>
<p><a href="https://codereview.chromium.org/1042923003/"><img width="656" src="/images/chromium/codereview_1.png" alt="Sample CL as it renders on codereview" /></a></p>
<p>If you want to test that your CL didn’t break anything, run <code class="language-plaintext highlighter-rouge">git cl try</code>. This will look at what your code touches and run a whole bunch of tests on a whole bunch of platforms.</p>
<p>In Chromium, code lands only after it’s been <code class="language-plaintext highlighter-rouge">LGTM-ed</code>, which means that someone has reviewed it and gave you the thumbs up. If you don’t know to whom to send it for review, pick someone
from your file’s OWNERS. In this case, look at the OWNERS file in <code class="language-plaintext highlighter-rouge">chrome/browser/ui/cocoa</code>. Owners are people who are responsible for the code,
so they tend to know it best. If they’re too busy for a review or aren’t
entirely familiar with your particular part of the code, they can direct you to
a better reviewer.</p>
<p>(Side note: please don’t <em>actually</em> send this pony code out for review. People will be very confused, and not necessarily amused.)</p>
<h2 id="ship-it-squirrel-">Ship it, squirrel! <img class="emoji" title=":shipit:" alt=":shipit:" src="https://assets-cdn.github.com/images/icons/emoji/shipit.png" height="30" width="30" /></h2>
<p>When your CL is reviewed and ready to go, all you have to do is check the “Commit” checkbox, and the commit-queue bots will take care of it. This means that they will run a whole bunch of unit tests again, and if they all pass (or “come up green”), merge your code into the current master branch.</p>
<h4 id="oh-noes-trouble">Oh noes, TROUBLE</h4>
<p>Sometimes, something goes wrong (or even better, horribly wrong). Your bots could come up red, and then you’ll
get an email from the <code class="language-plaintext highlighter-rouge">commit-bot</code> telling you your CL couldn’t land, because you done bungled some tests. This is totally fine – you didn’t break anything yet, because your code wasn’t merged.
Either your tests are actually broken, or some code got committed before you
and you need to rebase, or maybe you’ve just encountered a flaky test and don’t need to do anything. You can go back to your CL, fix your tests, and re-check the
“Commit” box.</p>
<p>Sometimes you’ll even get to break the tree. I recently tried to land a change
where all my bots were green and as soon as the change landed, it broke 165 tests on each bot. Suuuup.</p>
<p>It happens. You can revert your CL if you realize this in time, or that day’s sheriff might do it for you (especially if you’re an external committer, aren’t on IRC, etc). In this case, be nice to the sheriff and apologize a bit. Maybe send them a gif. Remember: if you’re stuck, ask for help!</p>
<h2 id="good-first-bugs">Good first bugs</h2>
<p>We have a <a href="https://code.google.com/p/chromium/issues/list?can=2&q=Hotlist%3DGoodFirstBug+&colspec=ID+Pri+M+Cr+Status+Owner+Summary+OS+Modified&x=m&y=releaseblock&cells=tiles">list</a> of bugs deemed as “good starter bugs”. Sometimes they’re more complicated that we thought, so don’t panic if that’s the case. It’s not you, it’s Chrome :). Protip: in the statuses, “Assigned” with a name in the owner means someone is actually looking at that issue, so it’s probably not a great one to pick.</p>
<h2 id="your-turn">Your turn!</h2>
<p>That’s it! That’s how you commit code to Chromium! Good luck, and if you do end up landing a CL, send me an <a href="mailto:noms@chromium.org">email</a> or a <a href="https://twitter.com/notwaldorf">tweet</a>. I’d love to see it!</p>
<h2 id="slides">Slides</h2>
<script async="" class="speakerdeck-embed" data-id="e0aa6a0aee1a4a20bd8962ae1d05fa81" data-ratio="1.77777777777778" src="//speakerdeck.com/assets/embed.js"></script>
Cat-DNS: learning about DNS with cats2014-08-11T00:00:00+00:00https://meowni.ca/posts/cat-dns-cascadia<p>I talked about <a href="https://github.com/notwaldorf/cat-dns">Cat-DNS</a> at <a href="http://2014.cascadiajs.com/">Cascadia.js</a>, and it wasn’t terrible! There is a video. Of me talking! On the internet! What a future we live in.</p>
<h2>=^..^=</h2>
<p>The internet needs more cats. DNS servers are the authority on all things internet.
Therefore, the best DNS server is the one that resolves everything to cats. This talk is about that.</p>
<p>We’re going to walk through the basics and find out how DNS servers work, how you can talk to a
DNS server if you’re a browser, and how to talk back to a browser if you are a DNS server. I’ll show you how you
can write your own DNS server in less than 200 lines of JavaScript, but perhaps most importantly, why you probably
shouldn’t.</p>
<p>And have I mentioned the cats? There are definitely cats.</p>
<h2 id="video">Video</h2>
<iframe width="640" height="360" src="//www.youtube.com/embed/qDPhW9P44fI" frameborder="0" allowfullscreen=""></iframe>
<h2 id="slides">Slides</h2>
<script async="" class="speakerdeck-embed" data-id="0a9dcc20fbdc013102b94a47441122ce" data-ratio="1.77777777777778" src="//speakerdeck.com/assets/embed.js"></script>
I don't really want to learn lldb, I just want to fix a crash2014-06-23T00:00:00+00:00https://meowni.ca/posts/unscary-lldb<p><code class="language-plaintext highlighter-rouge">lldb</code> stands for Llama-DB, and is a database of llamas you can use to debug programs
compiled with <code class="language-plaintext highlighter-rouge">clang</code> (lldb is to clang like gdb is to gcc). If you already know how to use <code class="language-plaintext highlighter-rouge">gdb</code>, then
here’s a <a href="https://developer.apple.com/library/mac/documentation/IDEs/Conceptual/gdb_to_lldb_transition_guide/document/lldb-command-examples.html">translation</a> of the common commands.</p>
<p><strong>Disclaimer</strong>: There is a <em>ton</em> of tutorials and pages about all of the
awesome features and commands of <code class="language-plaintext highlighter-rouge">lldb</code>, and how to become a debugging pro.
This is not that. This is the smallest set of things you need to read
to answer the question “what’s making this shit crash”. That’s it.</p>
<h2 id="step-1-make-it-go">Step 1. Make it go</h2>
<p>If you want to pass a bunch of arguments to your executable <code class="language-plaintext highlighter-rouge">moose</code>, use</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="o">(</span>´ ▽`<span class="o">)</span>.。o♡ src on fix/moose-crash ☀ ❥ lldb <span class="nt">--</span> moose arg1 arg2
Current executable <span class="nb">set </span>to <span class="s1">'moose'</span> <span class="o">(</span>x86_64<span class="o">)</span>.</code></pre></figure>
<p>If you don’t have arguments, <code class="language-plaintext highlighter-rouge">lldb foo</code> is enough. This just tells <code class="language-plaintext highlighter-rouge">lldb</code>
which executable to care about, but it won’t actually start the process for you.</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="o">(</span>lldb<span class="o">)</span> run <span class="nt">--</span><span class="o">></span> Start or re-start your process
<span class="o">(</span>lldb<span class="o">)</span> <span class="nb">exit</span> <span class="nt">--</span><span class="o">></span> Stop your process.</code></pre></figure>
<h2 id="step-2-make-it-crash">Step 2. Make it crash</h2>
<p>Since we (me) are investigating a crash, the first thing you need is a stack trace that
tells you where the crash is. So, start your process in <code class="language-plaintext highlighter-rouge">lldb</code>, make it crash, and we’ll take it
from there.</p>
<p>Side bar: I literally typed this blog out while sorting out a crash in the
sign-in bits of Chromium, so all my screenshots are Chromium code. Do not panic.
Your code can crash just as well if you give it enough time and attention.</p>
<p>Once you hit your crash, <code class="language-plaintext highlighter-rouge">lldb</code> tells you something like this.
<img src="/images/2014-06-23/crash.png" alt="lldb crash" /></p>
<p>I can’t tell you how excited I am at that little arrow. It <em>almost</em> looks non-intimidating. Almost.</p>
<h2 id="step-3-breakpoints-its-hammer-time">Step 3. Breakpoints! It’s hammer time</h2>
<p>The first thing I did was set a breakpoint at that line to figure out
what’s going on right before things got crashy (because I’m sure you’re dying to
know, my crash was happening because we hit that <code class="language-plaintext highlighter-rouge">DCHECK</code> which reads
“the item should always be signed in” and, spoilers, it isn’t)</p>
<p>To set a breakpoint in a file at a specific line:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="o">(</span>lldb<span class="o">)</span> breakpoint <span class="nb">set</span> <span class="nt">--file</span> profile_chooser_controller.mm <span class="nt">--line</span> 1509</code></pre></figure>
<p>Awesome discovery: you don’t have to give the full path to the file (which in
Chromium is a nightmare). Also, there’s autocomplete, so <code class="language-plaintext highlighter-rouge">profile_<tab></code> gives you
suggestions and happy feelings.</p>
<p>At this point, I pressed enter a bunch of times (as you do), which ended up
setting that breakpoint 4 times (as it does). So don’t do that. If you ignored that
bit of advice, here’s some helpful breakpoint-related things you can talk to <code class="language-plaintext highlighter-rouge">lldb</code> about:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="o">(</span>lldb<span class="o">)</span> breakpoint list <span class="nt">--</span><span class="o">></span> numbered list of all dem breakpoints
<span class="o">(</span>lldb<span class="o">)</span> breakpoint <span class="nb">help</span> <span class="nt">--</span><span class="o">></span> this is surprisingly not scary!
<span class="o">(</span>lldb<span class="o">)</span> breakpoint delete 4 <span class="nt">--</span><span class="o">></span> deletes the 4th breakpoint
<span class="o">(</span>lldb<span class="o">)</span> breakpoint delete <span class="nt">--</span><span class="o">></span> deletes ALL the things. but warns you first.</code></pre></figure>
<p>If you don’t like typing, all these commands have super l33t
shortcuts like <code class="language-plaintext highlighter-rouge">br s -f moose.c -l 12</code>. This intimidates me seven ways to Sunday,
since I’ve <em>just</em> learnt how to set a breakpoint, and I had to do it on the command
line and I like UIs, but if that’s your jam,
<a href="https://developer.apple.com/library/mac/documentation/IDEs/Conceptual/gdb_to_lldb_transition_guide/document/lldb-command-examples.html">here</a> is that list.
Go forth and conquer.</p>
<p>If you’ve already started your executable and forgot to set a breakpoint, it’s OK!
Just <code class="language-plaintext highlighter-rouge">Ctrl+C</code> in <code class="language-plaintext highlighter-rouge">lldb</code> to pause your program and clean up your room before
your friends come over. When you’re done, type <code class="language-plaintext highlighter-rouge">continue</code> to resume your process. Amazing!</p>
<h2 id="intermission-playing-with-stack-traces">Intermission: Playing with stack traces</h2>
<p>Current status: we’ve run our code, we’ve hit the breakpoint. <code class="language-plaintext highlighter-rouge">lldb</code> rewards you
with a snippet of code around that point. Dat arrow. ❤︎</p>
<p><img src="/images/2014-06-23/breakpoint.png" alt="lldb breakpoint" /></p>
<p>More things that you can probably want to do here:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="o">(</span>lldb<span class="o">)</span> thread backtrace <span class="nt">--</span><span class="o">></span> stack trace of how we got here
<span class="o">(</span>lldb<span class="o">)</span> up <span class="nt">--</span><span class="o">></span> move up the stack trace to your parent callee</code></pre></figure>
<h2 id="step-4-poke-at-things">Step 4: Poke at things</h2>
<p>Once you’re paused, you can inspect the value of a variable in that scope. You
probably want to do that because 90% of the time a thing that shouldn’t be null
is null, because C++.</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="o">(</span>lldb<span class="o">)</span> frame variable viewMode_</code></pre></figure>
<p>To step through things, get in the llama car, close the door, and start navimagating:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="o">(</span>lldb<span class="o">)</span> step <span class="nt">--</span><span class="o">></span> step into the call at this line. Alias <span class="k">for</span> <span class="s1">'thread step-inst'</span>
<span class="o">(</span>lldb<span class="o">)</span> next <span class="nt">--</span><span class="o">></span> skip to the next line. Alias <span class="k">for</span> <span class="s1">'thread step-in'</span>
<span class="o">(</span>lldb<span class="o">)</span> finish <span class="nt">--</span><span class="o">></span> step out of this call. Terrible <span class="nb">alias </span><span class="k">for</span> <span class="s1">'thread step-out'</span></code></pre></figure>
<p>If at any point you want to restart your process, just type <code class="language-plaintext highlighter-rouge">run</code> again. Don’t worry,
it’s not destructive – you’ll be asked to confirm if you really want to blow
away the frame you’re in. Bam!</p>
<h3 id="thats-it">That’s it!</h3>
<p>For realsies! You and <code class="language-plaintext highlighter-rouge">lldb</code> should probably get friendship bracelets now.</p>
<pre>
∩∩
(゚ω゚) .。o♡
│ │
│ └─┐○
ヽ 丿
∥ ̄∥
</pre>
<p> </p>
Presenter notes that don't suck2014-05-17T00:00:00+00:00https://meowni.ca/posts/presenter-notes-that-dont-suck<p>You’ve given a talk. It went great, and now everyone wants to see the slides. Hurray! Before we do anything else: give yourself a high-five. Giving a talk is hard, and you did it! This is something to be proud about.</p>
<p>Here’s the thing about slides, though: if you did them right, your slides should be pretty sparse. They’ll have very few words, probably in a big font, and some slides will only make sense if you’re talking along. This is extra sad if your presentation wasn’t recorded, or you didn’t record your voice: you’ll give your slides to the Internets and the Internets will be confused.</p>
<p>At this point, you could be tempted to just add a ton of content to your slides. Please don’t. Fewer words on a slide will force people to listen to <em>you</em> and not your slide. And that’s where the gems are.</p>
<h2 id="starting-point">Starting point</h2>
<p>This is the slide I’ll be using as an example. It’s part of <a href="https://speakerdeck.com/notwaldorf/how-chrome-keeps-users-happy-six-truths-and-a-lie">this</a> presentation I just gave about keeping users happy. On its own, it’s a fine looking slide, but unfortunately, it lacks some context. 1% of what? Am I talking about 1% of users? Am I saying I have 1% of all the users? Noooobody knows.</p>
<p><img src="/images/2014-05-17/original-slide.png" alt="original slide" style="width: 400px;" /></p>
<h2 id="attempt-the-first-presenter-notes">Attempt the first: presenter notes</h2>
<p>The first thing anyone will tell you (I know this because I <a href="https://twitter.com/notwaldorf/status/467382129828311040">asked</a> Twitter, and this is the first thing it told me) is “export your speaker notes”. I’m making the crazy assumption here you have some presenter notes. If you’re anything like me, you probably have to clean them up a little because the world isn’t ready for how your brain actually works.</p>
<p>So you export your presentation with presenter notes out of Keynote and into a PDF, and the result is pretty much the worst:</p>
<p><img src="/images/2014-05-17/exported-presenter-notes.png" alt="exported presenter notes" /></p>
<p>Look at all that whitespace! Look how tiny your slide is! Look how that text spans the entire page but it’s in the tiniest font ever. LOOK AT IT. It’s a good time to stop looking at it when your eyes are bleeding. If you upload this presentation into something like Speakerdeck, it will look absolutely ridiculous. Are you angry yet?</p>
<h2 id="attempt-the-second-in-slide-presenter-notes">Attempt the second: in-slide presenter notes.</h2>
<p>Well, if Keynote can’t be civilized about it, you can take the presenter notes and just paste them at the bottom of the slide. If you set a white background, it even ends up looking like whatever Keynote is attempting, but failing, to achieve. Look! Free consulting for Keynote.</p>
<p><img src="/images/2014-05-17/white-plain-text.png" alt="plain text" style="width: 400px;" /></p>
<h2 id="attempt-the-next-fancy-text">Attempt the next: fancy text</h2>
<p>That was ok, but you might run into some problems if your slide background is white, in which case the text will look like it’s part of the slide, rather than a helpful note. So if the problem is that the text is unstyled, let’s style it and make it look like a note! This is the next thing I came up with:</p>
<p><img src="/images/2014-05-17/fancy-note.png" alt="fancy text" style="width: 400px;" /></p>
<p>It’s a little better, but I think this can still end up looking weird if your slide design isn’t a title-subtitle sort of slide. You might have to spend some time and figure out where to best place that text box.</p>
<h2 id="alternative-post-it-notes">Alternative: post-it notes</h2>
<p>You can also go for a classic post-it, and slap it wherever you have space. The good news is: everyone knows what a post-it is and they won’t think you designed your slide like that. Whew.</p>
<p><img src="/images/2014-05-17/post-it.png" alt="post-it note" style="width: 400px;" /></p>
<h2 id="disclaimer">Disclaimer</h2>
<p>I don’t know which way is better; I think it will depend a lot on your slide deck design. I just wanted you to know that you don’t have to let Keynote terrorize you with its horribly exported presenter notes. There are alternatives.</p>
Static initializers will murder your family2014-04-22T00:00:00+00:00https://meowni.ca/posts/static-initializers<p>But only if your family is code.</p>
<p>So this is a bit of a terrible blog post because a) it’s about a really obscure atrocity that happens in C++ (as opposed to the common atrocities that happen in C++ on the regs) and b) there are not enough funnies in the world to make up for it. I recommend skipping it if you’ve just eaten, are feeling light-headed, or don’t want to make eye contact with C++. As a general policy, you should probably never make eye contact with C++. It can smell fear.</p>
<h2 id="programmer-meet-static-initializers">Programmer, meet static initializers</h2>
<p>We’re going to be talking about static class objects, or objects defined in a global/unnamed namespace, such as these fellas:</p>
<figure class="highlight"><pre><code class="language-c--" data-lang="c++"><span class="k">namespace</span> <span class="p">{</span>
<span class="k">static</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">kSquirrel</span> <span class="o">=</span> <span class="s">"sad squirrel"</span><span class="p">;</span>
<span class="k">static</span> <span class="k">const</span> <span class="n">Superhero</span> <span class="n">batman</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// or</span>
<span class="k">class</span> <span class="nc">Foo</span> <span class="p">{</span>
<span class="k">static</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">panda_</span> <span class="o">=</span> <span class="s">"also a sad panda"</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>
<p><em>Static initialization</em> is the dance we do when creating these objects. This is not a dance we do when we initialize things with <em>constant</em> data (like <code class="language-plaintext highlighter-rouge">static int x = 42</code>); the compiler sees that the thing after the <code class="language-plaintext highlighter-rouge">=</code> is constant and can’t change, so it can inline it. However, if you try to initialize a variable by running code (e.g. <code class="language-plaintext highlighter-rouge">static int x = foo()</code>), then this is not a constant anymore, and it will result in a static initializer. In C++11, I think <code class="language-plaintext highlighter-rouge">constexpr</code> will let you hint to the compiler that the thing after the equal is a constant expression, if it is that, so it can compute it at compile-time. I don’t get to use a lot of C++11, so this is still about nightmares of C++ past, and I don’t think <code class="language-plaintext highlighter-rouge">constexpr</code> will do away with all of the murders anyway. Finally, the compiler promises you to run all the static initializers before the body of <code class="language-plaintext highlighter-rouge">main()</code> is executed. That, unfortunately, doesn’t mean much.</p>
<h2 id="why-static-initializers-are-bad-news-bears">Why static initializers are bad news bears</h2>
<p>As Douglas Adams, the inventor of C++ said, static initializers have “made a lot of people very angry and been widely regarded as a bad move”. Apart from being hard to spell, they tend to throw up on your shoes:</p>
<ul>
<li>Static variables in the same <em>compilation unit</em> (or the same file) will be constructed in the order they are defined. This means that this code is predictable, and always does exactly what you think it does. This is also the last of the good news:</li>
</ul>
<figure class="highlight"><pre><code class="language-c--" data-lang="c++"><span class="k">namespace</span> <span class="p">{</span>
<span class="k">static</span> <span class="n">Superhero</span> <span class="n">batman</span><span class="p">;</span>
<span class="k">static</span> <span class="n">Superhero</span> <span class="n">robin</span> <span class="o">=</span> <span class="n">batman</span><span class="p">.</span><span class="n">getSidekick</span><span class="p">();</span>
<span class="p">}</span></code></pre></figure>
<ul>
<li>Static variables in <em>different</em> translation units are constructed in an undefined order. This is so terrible it has its own name: the <a href="http://www.parashift.com/c++-faq/static-init-order.html">static initialization order fiasco</a>. It goes like this:</li>
</ul>
<figure class="highlight"><pre><code class="language-c--" data-lang="c++"> <span class="c1">// In x.cpp:</span>
<span class="k">static</span> <span class="n">Superhero</span> <span class="n">batman</span><span class="p">;</span>
<span class="c1">// In y.cpp:</span>
<span class="k">static</span> <span class="n">Superhero</span> <span class="nf">robin</span><span class="p">(</span><span class="n">batman</span><span class="p">.</span><span class="n">getSidekick</span><span class="p">());</span>
<span class="c1">// If that wasn't believable, imagine it was something like:</span>
<span class="c1">// static Superhero robin(BestSuperhero::batman);</span>
<span class="c1">// where BestSuperhero is a namespace or a static class and</span>
<span class="c1">// you call batman.getSidekick() in robin's constructor.</span>
</code></pre></figure>
<p>Yup. That’s it. Whether <code class="language-plaintext highlighter-rouge">x.cpp</code> or <code class="language-plaintext highlighter-rouge">y.cpp</code> gets compiled first is not defined (because C++), which means if <code class="language-plaintext highlighter-rouge">y.cpp</code> gets compiled first, <code class="language-plaintext highlighter-rouge">batman</code> hasn’t been constructed. You know what happens when you call <code class="language-plaintext highlighter-rouge">getSidekick()</code> on an uninitialized object? Regrets happen.</p>
<ul>
<li>We’re not done yet. Why have insanely terrible code when you can have insanely terrible EXPENSIVE code! Evan Martin has a really, really good <a href="http://neugierig.org/software/chromium/notes/2011/08/static-initializers.html">post</a> about this, but the tl;dr is that because the static initializers need to happen before <code class="language-plaintext highlighter-rouge">main()</code>, that code needs to be paged, which leads to disk seeks, which leads to awful startup performance. Seriously, read Evan’s post because it’s amazing.</li>
</ul>
<h2 id="spotting-static-initializers-in-the-wild-an-incomplete-manual">Spotting static initializers in the wild: an incomplete manual</h2>
<p>Here are some examples of things that are and aren’t static initializers, so
that at least we know what we’re looking for before we try to fix them.</p>
<figure class="highlight"><pre><code class="language-c--" data-lang="c++"><span class="c1">// Both of these are ok, because 0 is a compile time constant, so it can't</span>
<span class="c1">// change. The const doesn't make a difference; it's the thing after</span>
<span class="c1">// the = sign that makes the difference.</span>
<span class="k">static</span> <span class="k">const</span> <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">static</span> <span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="c1">// Below, both the pointer and the chars in the string are const, so the</span>
<span class="c1">// compiler will treat this as a compile-time constant. So this is ok</span>
<span class="c1">// because both the thing before and after the = sign are constant.</span>
<span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="n">panda</span><span class="p">[]</span> <span class="o">=</span> <span class="s">"happy panda"</span><span class="p">;</span>
<span class="c1">// This, however, calls a constructor, so it's not ok.</span>
<span class="k">static</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">sad_panda</span> <span class="o">=</span> <span class="s">"sad panda"</span><span class="p">;</span>
<span class="k">static</span> <span class="kt">int</span> <span class="n">a</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="c1">// This is not ok, because the thing after the = sign isn't a const,</span>
<span class="c1">// so it can change before b is initialized.</span>
<span class="k">static</span> <span class="kt">int</span> <span class="n">b</span> <span class="o">=</span> <span class="n">a</span><span class="p">;</span>
<span class="c1">// This has to call the Muppet() constructor, and who knows what that</span>
<span class="c1">// does, so it's definitely not a const, and a case of the static initializers.</span>
<span class="k">static</span> <span class="n">Muppet</span> <span class="n">waldorf</span><span class="p">;</span></code></pre></figure>
<h2 id="thems-the-breaks">Them’s the breaks</h2>
<p>There’s a couple of ways in which you can fix this, some better than others:</p>
<ul>
<li>The best static initializer is no static initializer, so try const-ing all your things away. This will take you as far as defining an array of strings, for which you can’t pray the initializer away. (Trivia: Praying The Const Away™ is what I call a <code class="language-plaintext highlighter-rouge">const_cast</code>)</li>
<li>Place all your globals in the same compilation unit (i.e. a massive <code class="language-plaintext highlighter-rouge">constants.cpp</code> file). You can certainly try this, but if your project is the giant Snuffleupagus that Chrome is, you might be laughed at</li>
<li>Place the static globals inside the function that needs them (or, if they’re the village bicycle, make a getter for them), and define them as function-static variables. Then you know they will be initialized only once, the first time that function is called. Whenever it is called</li>
</ul>
<p>That last bullet sounds like black magic, so here’s an example. This is the static initializer that we are trying to fix. Convince yourself that this code is no good:</p>
<figure class="highlight"><pre><code class="language-c--" data-lang="c++"><span class="k">namespace</span> <span class="p">{</span>
<span class="k">static</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">bucket</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="s">"apples"</span><span class="p">,</span> <span class="s">"pears"</span><span class="p">,</span> <span class="s">"meerkats"</span><span class="p">};</span>
<span class="p">}</span>
<span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="nf">GetBucketThing</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">bucket</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
<span class="p">}</span></code></pre></figure>
<p>We can fix it by moving <code class="language-plaintext highlighter-rouge">bucket</code> into <code class="language-plaintext highlighter-rouge">GetBucketThing()</code>:</p>
<figure class="highlight"><pre><code class="language-c--" data-lang="c++"><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="nf">GetBucketThing</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Sure, it's a non-trivial constructor, but it will get called once,</span>
<span class="c1">// the first time GetBucketThing() gets called, which will be at runtime</span>
<span class="c1">// and therefore a-ok.</span>
<span class="k">static</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">bucket</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="s">"apples"</span><span class="p">,</span> <span class="s">"pears"</span><span class="p">,</span> <span class="s">"meerkats"</span><span class="p">};</span>
<span class="k">return</span> <span class="n">bucket</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
<span class="p">}</span></code></pre></figure>
<p>Yup. That’s pretty much it. If you want more reading on the topic, here’s a neat chromium-dev <a href="https://groups.google.com/a/chromium.org/forum/#!topic/chromium-dev/p6h3HC8Wro4">thread</a> discussing this in more details (and talking about when these static globals are actually cleaned up).</p>
<h2 id="mmmmkay">Mmmmkay.</h2>
<p>I don’t know why you’ve made it this far. Maybe you thought there was going to be a joke or a prize at the end. There isn’t. There’s just this gif, and you could’ve just scrolled down for it.</p>
<p><img src="https://media.giphy.com/media/3boPPdHk2ueo8/giphy.gif" alt="puppy" /></p>
Presentation slides and writer's block2014-04-09T00:00:00+00:00https://meowni.ca/posts/slides-writers-block<p>I am the poster child for writer’s block. I can’t write the <code class="language-plaintext highlighter-rouge">#ifdef</code> header guard correctly for a brand new C++ class, I don’t remember the order of the <code class="language-plaintext highlighter-rouge">public static void main args</code> incantation in Java, and for the life of me, I can’t start working on an empty presentation. Not even if you promise me pizza. (Please promise me pizza though.)</p>
<h2 id="start-with-an-outline">Start with an outline</h2>
<p>There’s already an amazing <a href="http://speaking.io/plan/an-outline/">speaking.io post</a> about writing outlines which you should read. The tl;dr is that you start with the top-level things that you want to talk about (feel free to <code class="language-plaintext highlighter-rouge">s/2Pac/Biggie/g</code> in that example if a) you have good taste or b) you’re from the East Coast because c) yolo), and then slowly, but with gumption, start developing dem ideas.</p>
<p>I personally take that post literally and write my outline as a markdown gist. The top-level ideas are ## headers, the supporting points are sub-bullets, and there’s a lot of “???. Profit!” and notes to future-Monica, because present-Monica is usually a jerk.</p>
<h2 id="time-to-procrastinate">Time to procrastinate</h2>
<p>This is great! You have an outline! You deserve a break. No, really; take a break.</p>
<p>I let this baby stew for a couple of days, so that it gets nice and tender. Nobody likes an undercooked outline. It’s also not a terrible idea at this point to ask some of your friends to read your outline and tell you if this would be a talk they wouldn’t hate listening to.</p>
<h2 id="time-for-skeletor">Time for Skeletor™</h2>
<p>Once you think you’ve procrastinated enough and it’s time to actually work on your presentation, you’ll be tempted to start thinking about slide design, typefaces, and the kind of cat gifs you’ll include for bonus points.</p>
<p>Don’t.</p>
<p>I mean, definitely include cat gifs, but that time isn’t now. I’m pretty sure “being OCD about typography” is correlated with “being really good at writer’s block”, so I can promise that you’ll waste many evenings choosing drop shadows before you realize how screwed you are for content.</p>
<p>I’ve sorted this out by having a <em>really</em> basic presentation <a href="/includes/skeletor.pdf">template</a>, affectionately named Skeletor, that I only use when I work on the content. It’s got a font that doesn’t make me angry, a really basic colour scheme, and I can distinguish between title and filler slides.</p>
<script async="" class="speakerdeck-embed" data-id="37416630a2e901311a744eb53c3a6292" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"></script>
<p>Then, I dump the stewed outline into this presentation – the titles, the bullets, the question marks – and do a dry-run. So that the rehearsal is actually useful, you should probably make sure your (entirely empty) presenter’s notes are visible and editable. If you don’t use presenter’s notes, I don’t even know how you exist. Get some paper.</p>
<h2 id="the-forced-rehearsal">The forced rehearsal</h2>
<p>“But Monica”, you’ll say, “you barely have any content, what are you even rehearsing?”. And my answer to that is: don’t start sentences with “but”. Also, it turns out that even if you haven’t thought about it before, when you have to start speaking about a slide that says “2Pac <3s the Bay Area”, your first instinct will be to panic and explain why that’s true. There’s the content! You’ll make up some facts, some funnies, some things you should have researched but didn’t because you probably watched House of Cards instead, and you will promptly type these out in your presenter’s notes. Then go back, change your slides/notes, and rehearse again. Bingo-bango, sugar in the gas tank, you’re pretty much done.</p>
<p>Guess who has a non-empty presentation now, with slides and content? GUESS. (Hint: it’s you).</p>
<p>Now go fix them fonts.</p>
<h3 id="-ヾノ-">♬♪♫ ヾ(*・。・)ノ ♬♪♫</h3>
Code reviews for fun and profit2014-03-31T00:00:00+00:00https://meowni.ca/posts/code-reviews<h2 id="stats-a-preamble">Stats: a preamble</h2>
<p>I’ve been reading too much about March Madness brackets, so I thought I had to run some numbers around here like the cool kids do. Get your umbrella out, it’s about to rain cold facts.</p>
<p>In the history of time, <a href="http://www.ohloh.net/p/chrome">Chromium</a> has had 205,095 commits made by 1,943 contributors representing 7,431,088 lines of code. In the last 30 days, there have been 5021 commits, by 637 contributors, including 53 new hoomans.</p>
<p>I did some advanced Nate Silver analysis here for you, and that’s at least 167 commits and 1+ new committers a day. On average, that’s at least 7 commits an hour. Every hour. All of the hours.</p>
<p>That’s an imperial ton of new code being added, by what it seems like new people. Imagine if everyone could commit code willy-nilly. Are you imagining a minefield? You should.</p>
<h2 id="code-reviews-ftw">Code reviews ftw</h2>
<p>Good news for our browser using audience! Chromium isn’t a minefield, and on top of it, has pretty awesome looking code. This comes from the fact that any code changes need to be reviewed and blessed before they can land on the master branch. More eyes means less bugs means you’re less likely to commit broken code and break the internet. And you really don’t want to break the internet.</p>
<p>Even if you have tests, and everything is going your way, you can write correct, but genuinely shitty code. 7 million lines of kinda-shitty code is not something anyone wants to work with, and are worth investing a little time in fixing.</p>
<p>Code reviews also bring up the bus factor, which is my favourite sinister nerd metaphor. You know, the <em>buuuuuus</em> factor. The number of people that can get run over by a bus on a team before that team is royally and epically screwed. If all the code that you write has been closely read by a different person, then you’re probably ok getting run over by a bus every once in a while. But still, you probably shouldn’t. Who would feed your cat?</p>
<h2 id="consistent-code-is-the-best-code">Consistent code is the best code</h2>
<p>Code <a href="http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml">style guides</a> are sooper neat, and are a huge part of code reviews, because ain’t nobody got time to argue about braces. You can spend that time arguing about imperative vs. functional languages, which is a much better use of everyone’s time. Having a strict and detailed style guide means that, even though it’s probably going to piss off some very opinionated people, all of the code will look the same, all the time, regardless of who wrote it. You can jump into any area of Chromium and feel at home, because nobody went crazy with the whitespace.</p>
<p>And trust me, it only takes a week to get over the wrong kind of braces.</p>
<h2 id="dont-be-scared">Don’t be scared</h2>
<p>We’ve reached the point in the blog post where I confess I am a terrible code reviewer. I am incredibly scared of reviewing code I haven’t written. Guess what: in the case of Chromium, that’s most of it.</p>
<p>Reviewers are usually picked from a list of <a href="http://dev.chromium.org/developers/owners-files">owners</a>, which is a group of people that is intimate enough with that area of code that they’ve taken it out to dinner a couple of times. They’re the ones that have the final say on whether the code is ok, and who make sure that little neighbourhood of code isn’t a minefield. Even if you’re not an owner, your team mates will probably ask you for a first review of their code, to make the owners’ lives easier. You will be tempted to panic and not want to take responsibility for it. You will be tempted to run away from confrontation and agree with all of their changes.</p>
<p>Don’t. It doesn’t help anyone, and it will be like you’re not even there. You were asked for a review because people trust your judgement and value your opinion. So give it. The worst thing that can happen is that they will disagree, and you will have a polite conversation about it. The best thing that can happen is amazingly awesome code. So, have a little courage, and be the little reviewer that could.</p>
<h2 id="dont-be-a-jerk">Don’t be a jerk</h2>
<p>Jerks are the worst code reviewers. Generally, people tend to get very defensive when faced with criticism, and they’ll get exponentially more defensive if that criticism comes in a harsh, patronizing voice. Defensive people aren’t open to discussions, and it will make the review experience painful for everyone.</p>
<p>Don’t be a control freak either. You might disagree with the names of variables and functions, but unless you have good suggestions, you might want to consider conceding those points.</p>
<p>The moral here is: the code you’re reviewing was written by a smart human. Treat them like one.</p>
<h2 id="what-makes-a-great-code-reviewer">What makes a <em>great</em> code reviewer</h2>
<p>Good code reviewers are diligent: they enforce the style guide, they make sure you’ve documented the new code, and they aren’t scared of making you shave a yak or four if that’s needed (this includes both yaks that you have conjured and the ones you’ve accidentally stumbled upon).</p>
<p>Even better reviewers will try to help the author of the code learn. This is a very hard stack of plates to balance: on one hand you don’t want to be lazy and offer really vague advice that will waste the programmer’s time, and on the other hand you don’t want to spoon-feed them every single character of code. I don’t think there’s a magic formula here: this comes with experience, and with your knowledge of the person you’re reviewing.</p>
<p>The best code reviewers are usually right, and always humble. They’ll always admit when they’re wrong and they’ll back away from points that are too annoying (“yes, the way I suggested is better, but I see your point that it’s way, way too much effort, and I agree it’s not worth it”).</p>
<h2 id="code-reviews-are-kind-of-social">Code reviews are kind of social</h2>
<p>A neat/fun thing that happens even in a project as big as Chromium is that programmers and reviewers will form specific reviewing relationships.</p>
<p>My favourite reviewer used to leave pretty vague comments when the general approach of one of my patches was bad. At first the comments didn’t make any sense, but because I didn’t want to look dumb, I’d spend four hours trying to figure out what they meant. Somebody who isn’t me would probably just go back and ask for a clarification, and that would define their reviewing relationship. However, I kind of really enjoy this sort of code sherlocking, because by the time I figured out what they meant, I would have learned a whole bunch of new things, fixed my code, replaced it with badass code, and would be genuinely excited. And that’s why they’re <em>my</em> favourite reviewer, but not necessarily the best reviewer. Our styles just match really well.</p>
<h2 id="how-to-level-up-as-a-code-reviewer">How to level up as a code reviewer</h2>
<p>First, become comfortable with the style guide. The first thing you should do for every review is go through all of the new code, and find all the nits. Is the indentation ok? Do the variable names follow the naming convention? Have new functions or parameters been documented correctly?</p>
<p>Once you’re done with that, ask yourself if you understand what the code does. If you can’t, the next person won’t either. It’s easy to think you’re dumb and the code is great, but that’s almost never true. Does the new code make sense where it is? Should it be in a different class? Should it be a class or a helper function? Is this code duplicated anywhere else?</p>
<p>Make sure that if the code can be tested (this, sadly, isn’t always true), it is tested. Don’t be afraid to ask for tests if they’re not there.</p>
<p>Finally, ask yourself if tomorrow you’d be comfortable fixing a bug in the code. If it looks scary or confusing to you, it will probably look scary or confusing to everybody else. Remember: only you can prevent forest fires!</p>
When pull requests get tricky2014-02-25T00:00:00+00:00https://meowni.ca/posts/tricky-pull-requests<p>Imagine this: you have forked a repo a long time ago and have since been in a happily committed relationship with the master branch, modifying files and committing from the hip.</p>
<p>In case you have doubts about the likelihood of this scenario, we’re talking about my <code class="language-plaintext highlighter-rouge">notwaldorf.github.com</code> blog branch, which I forked from <code class="language-plaintext highlighter-rouge">holman/left</code>. All my changes are on the master branch so that GitHub can do its magic gh-pages trick and just Make Things Work™. This could also happen for repos you’ve forked and mucked around with, but never thought you’d ever contribute to.</p>
<p>Because you’re a good open sorcerer, you might, at some point, want to send a pull request to fix a thing in the original repo. If you’re anything like I am, you are now in a bit of a terrible situation because your fork’s tip of tree has advanced and diverged quite a bit from the upstream repo, so any pull requests you send out will be polluted with code that doesn’t belong in them.</p>
<p>At this point, you probably have some regrets. Let’s not dwell on them. Here’s the set of steps that will help you navigate this minefield you find yourself in. They’re not special; I just couldn’t find them all in one place, and wanted a summary for the future. <a href="https://twitter.com/kmrhb">Kamal</a> figured most of this out, because he is a git wizard, while I am, most definitely, not.</p>
<h2 id="set-up-your-upstream">Set up your upstream</h2>
<p>If <code class="language-plaintext highlighter-rouge">git remote</code> doesn’t show you a branch called <code class="language-plaintext highlighter-rouge">upstream</code>, you need to add one:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git remote add upstream https://github.com/user/repo_you_forked.git
</code></pre></div></div>
<h2 id="set-up-a-clean-branch-for-your-fix">Set up a clean branch for your fix</h2>
<p>Step into your tardis and branch from when you were last in sync with the upstream. In my case, this was when I initially created my fork.
A dull perusing of <code class="language-plaintext highlighter-rouge">git log</code> or <code class="language-plaintext highlighter-rouge">git reflog</code> should point you to the right sha. Then,</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git checkout -b pr_branch
git reset --hard sha_from_the_past
</code></pre></div></div>
<h2 id="get-your-changes-in">Get your changes in</h2>
<p>Here you have two options. If your changes are tiny, or you know exactly what they are, you can just manually reapply them. And by that I mean copy paste the changes into the right files, like a barbarian. For the record, this is my preferred approach. I am a barbarian. I live in the git stone age.</p>
<p>Alternatively, you can go the fancy route with</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git checkout -p master file_to_modify
</code></pre></div></div>
<p>This will look at the diff between master (which is in the future), and your working copy (we are in the past) and let you pick and choose individual hunks. Having taken a moment and appreciated how amazing this last sentence was, you can:</p>
<ul>
<li>hit <em>s</em> to split the hunks into smaller hunks</li>
<li>hit <em>y</em> or <em>n</em> to pick or skip a hunk.</li>
</ul>
<p>If you’ve touched any files, it would be a good time to do your familiar <code class="language-plaintext highlighter-rouge">git add/git commit</code> dance.</p>
<h2 id="merge-the-upstream-changes-in">Merge the upstream changes in</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git fetch upstream # You won't see any changes in git log. Don't panic yet.
git merge upstream/master # Some wild upstream changes appear in git log.
</code></pre></div></div>
<h2 id="--t-t-t-test-your-cha-an-ges--">♫ ♪ T-t-t-test your cha-an-ges ♫ ♪</h2>
<h2 id="upload-your-branch">Upload your branch</h2>
<p>This is the last step. If you want, you can rename your branch before uploading it. I usually do, because my original branch names tend to be silly. After this, you can go and look at your branch in GitHub and be delighted with the progress you’ve made. Time to send out that pull request!</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git push origin pr_branch:possibly_new_branch_name
</code></pre></div></div>
<p><br />
Hope this helped!</p>
Ruby for Canadians: an instruction manual2014-02-25T00:00:00+00:00https://meowni.ca/posts/ruby-eh<p>One of the hardships about being Canadian is that most programming languages are quite simply, rude. Descriptions like ‘imperative’ and ‘declarative’ are enough to fill even the most impolite of Canadians with a vague sense of discomfort. Fear no more! Ruby is the sort of language that addresses all these concerns, and adds a familiar, maple-syrupy feel to your code. <a href="https://github.com/notwaldorf/ruby-eh">Here</a> are the codes, for your perusal.</p>
<h2 id="theres-no-such-thing-as-too-polite">There’s no such thing as ‘too polite’</h2>
<p>Ruby lets you open your classes and teach your fellow programmers some basic manners:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Object</span>
<span class="k">def</span> <span class="nf">please</span>
<span class="nb">self</span>
<span class="k">end</span>
<span class="kp">alias_method</span> <span class="ss">:eh</span><span class="p">,</span> <span class="ss">:please</span>
<span class="kp">alias_method</span> <span class="ss">:eh?</span><span class="p">,</span> <span class="ss">:please</span>
<span class="kp">alias_method</span> <span class="ss">:pardon</span><span class="p">,</span> <span class="ss">:please</span>
<span class="kp">alias_method</span> <span class="ss">:pardon?</span><span class="p">,</span> <span class="ss">:please</span>
<span class="k">end</span>
<span class="k">module</span> <span class="nn">Kernel</span>
<span class="kp">alias_method</span> <span class="ss">:sane_puts</span><span class="p">,</span> <span class="ss">:puts</span>
<span class="kp">alias_method</span> <span class="ss">:giver</span><span class="p">,</span> <span class="ss">:puts</span>
<span class="k">def</span> <span class="nf">puts</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
<span class="n">sane_puts</span> <span class="s2">"If you don't mind, "</span> <span class="o">+</span> <span class="n">s</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Now you can write correct and civilized code like you’ve always wanted:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">giver</span> <span class="s2">"maple syrup"</span><span class="p">.</span><span class="nf">please</span><span class="p">.</span><span class="nf">reverse</span><span class="p">.</span><span class="nf">eh?</span>
<span class="c1"># If you don't mind, purys elpam</span>
</code></pre></div></div>
<h2 id="dont-forget-about-quebec">Don’t forget about Quebec</h2>
<p>Ruby is the first language to pioneer French as the one true language. We have translated all the methods in <code class="language-plaintext highlighter-rouge">Array</code> and stored them in a dictionary called <code class="language-plaintext highlighter-rouge">translations</code>, that contains entries of the form <code class="language-plaintext highlighter-rouge">:english_foo => :french_foo</code>. A small iteration,</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Array</span>
<span class="no">Array</span><span class="p">.</span><span class="nf">instance_methods</span><span class="p">(</span><span class="kp">false</span><span class="p">).</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span>
<span class="k">if</span> <span class="n">translations</span><span class="p">.</span><span class="nf">key?</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="kp">alias_method</span> <span class="n">translations</span><span class="p">[</span><span class="n">f</span><span class="p">],</span> <span class="n">f</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>And we can write glorious code that would make Quebec proud</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">articles_aleatoires</span><span class="p">(</span><span class="n">matrice</span><span class="p">)</span>
<span class="n">matrice</span><span class="p">.</span><span class="nf">echantillon</span><span class="p">(</span><span class="mi">1</span> <span class="o">+</span> <span class="nb">rand</span><span class="p">(</span><span class="n">matrice</span><span class="p">.</span><span class="nf">compte</span><span class="p">))</span>
<span class="k">end</span>
<span class="n">articles_aleatoires</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">]</span>
<span class="c1"># => [1, 2]</span>
</code></pre></div></div>
<h2 id="canadian-slang">Canadian slang</h2>
<p>Fans of <a href="http://en.wikipedia.org/wiki/Bob_and_Doug_McKenzie">Bob and Doug McKenzie</a> can breathe a sigh of relief to know that Ruby speaks the language of the Great White North, eh?</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Exception</span>
<span class="k">alias</span> <span class="ss">:sane_to_s</span> <span class="ss">:to_s</span>
<span class="k">def</span> <span class="nf">to_s</span>
<span class="n">sane_to_s</span> <span class="o">+</span> <span class="s2">". Take off, hoser."</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="c1"># >> raise ArgumentError</span>
<span class="c1"># ArgumentError: uncaught throw ArgumentError. Take off, hoser.</span>
</code></pre></div></div>
<h2 id="digression-for-non-canadians">Digression for non-Canadians</h2>
<p>Of course, you can use this idea of open classes for truly magical features. Good code is nothing if not mildly interesting.</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Fixnum</span>
<span class="k">alias</span> <span class="ss">:sane_equals</span> <span class="ss">:==</span>
<span class="k">def</span> <span class="nf">==</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="n">not</span> <span class="nb">self</span><span class="p">.</span><span class="nf">sane_equals</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="c1"># >> 4 == 4</span>
<span class="c1"># => false</span>
<span class="c1"># >> 4 == 5</span>
<span class="c1"># => true</span>
</code></pre></div></div>
<h2 id="unicode">Unicode</h2>
<p>This is where this post starts being less Canadian and more flat out crazy.</p>
<p>Ruby is down with Unicode in identifiers. That’s because Ruby is down with pretty much everything. The only thing I have yet to convince Ruby to do is to let me alias keywords, and I’m pretty sure that’s just because I haven’t tried hard enough.</p>
<p>If you add a comment to gently nudge at an encoding, you too can write this production ready code:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># encoding: utf-8</span>
<span class="k">class</span> <span class="nc">Object</span>
<span class="k">def</span> <span class="err">☢</span>
<span class="n">sane_puts</span> <span class="s2">"BOOM"</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Guess what calling ☢ will do. GUESS.</p>
<p>Unicode can fill your boring, profesh code with whimsy:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="err">☆</span> <span class="o">==</span> <span class="err">☂</span>
<span class="nb">puts</span> <span class="err">☎</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Our advanced readers can also define a function named U+00A0, the non-breaking space, as seen <a href="http://www.rubyinside.com/the-split-is-not-enough-whitespace-shenigans-for-rubyists-5980.html">here</a>.</p>
<h2 id="bare-words">Bare words</h2>
<p>We all know that Ruby has bare words from Gary Bernhardt’s <a href="https://www.destroyallsoftware.com/talks/wat">Wat</a> talk, but did you know Ruby also allows you to have bare words as function names? Because that’s totally a thing you’d want. Thanks to <a href="http://99designs.com/tech-blog/blog/2012/10/30/abusing-ruby-for-fun-and-profit/">Richo Healey</a> for the example:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">self</span><span class="p">.</span><span class="nf">instance_exec</span> <span class="k">do</span>
<span class="k">def</span> <span class="nf">method_missing</span><span class="p">(</span><span class="n">sym</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="c1"># Splat args if passed in from a parent call</span>
<span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="nf">length</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">&&</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">Array</span><span class="p">)</span> <span class="o">&&</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">].</span><span class="nf">class</span> <span class="o">==</span> <span class="no">NameError</span>
<span class="n">args</span> <span class="o">=</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">end</span>
<span class="n">method_names</span><span class="p">,</span> <span class="n">arguments</span> <span class="o">=</span> <span class="n">args</span><span class="p">.</span><span class="nf">partition</span> <span class="p">{</span> <span class="o">|</span><span class="n">a</span><span class="o">|</span> <span class="n">a</span><span class="p">.</span><span class="nf">class</span> <span class="o">==</span> <span class="no">NameError</span> <span class="p">}</span>
<span class="nb">method</span><span class="p">([</span><span class="n">sym</span><span class="p">.</span><span class="nf">to_s</span><span class="p">,</span> <span class="o">*</span><span class="n">method_names</span><span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:name</span><span class="p">)].</span><span class="nf">join</span><span class="p">(</span><span class="s2">" "</span><span class="p">)).</span><span class="nf">call</span><span class="p">(</span><span class="o">*</span><span class="n">arguments</span><span class="p">)</span>
<span class="k">rescue</span> <span class="no">NameError</span> <span class="o">=></span> <span class="n">e</span>
<span class="k">return</span> <span class="p">[</span><span class="n">e</span><span class="p">,</span> <span class="o">*</span><span class="n">arguments</span><span class="p">]</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Defining such a function doesn’t even look that improper, which is why I recommend moving the above code into a separate .rb file, deep at the bottom of a folder barrel.</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="ss">:define_method</span><span class="p">,</span> <span class="ss">:"take off"</span><span class="p">)</span> <span class="k">do</span>
<span class="nb">puts</span> <span class="s2">"♫ ♪ Coo loo coo coo, coo coo coo coo ♬ ♪"</span>
<span class="k">end</span>
</code></pre></div></div>
<p>This leads to the most excellent of results, and a job well done:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">take</span> <span class="n">off</span>
<span class="c1"># >> If you don't mind, ♫ ♪ Coo loo coo coo, coo coo coo coo ♬ ♪</span>
<span class="c1"># => nil</span>
</code></pre></div></div>
<p><a href="https://github.com/duckinator">Nik Markwell</a> has a neat implementation of a saner, more <a href="https://gist.github.com/duckinator/d08df4d312139a447738">constrained</a> version of this, which ends up looking like</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">as</span> <span class="n">long</span> <span class="n">as</span> <span class="o">-></span> <span class="p">{</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">10</span> <span class="p">},</span> <span class="o">-></span> <span class="p">{</span> <span class="nb">puts</span> <span class="n">i</span> <span class="o">+=</span> <span class="mi">1</span> <span class="p">}</span>
</code></pre></div></div>
<p>However, being practical and sane isn’t the Canadian way. If it were, most Canadians wouldn’t live in a place where 11 months of the year the air hurts your face. We don’t stand for useful applications of bare functions, and nor should you.</p>
<h2 id="next-on-our-agenda">Next on our agenda</h2>
<p>Convincing the W3C that the California Style Sheets spelling of ‘colour’ and ‘grey’ is the only appropriate one. Do not lose faith, Canadians. Now that the rest of the world has accepted curling as a sport, they’re ready to accept anything.</p>
Cat-DNS: a DNS server that resolves everything to cats2014-02-18T00:00:00+00:00https://meowni.ca/posts/go-cat-dns-go<p>The internet needs more cats. DNS servers are the authority on all things internet. Therefore, the best DNS server is the one that resolves everything to cats. Guess what kind of DNS server this is (Hint: it’s the cat kind).</p>
<h2 id="making-it-go">Making it go</h2>
<p>First, get the <a href="https://github.com/notwaldorf/cat-dns">code</a>, and the npm packages you need (the instructions are with the code). To run, start the server as a privileged process. This is because to be a DNS server, you need to be a UDP server on port 53. This is a small numbered port, which means it needs superpowers. This is how your run it:</p>
<p><code>
sudo node cat-dns.js
</code></p>
<p>You also need to somehow set your DNS server to be localhost. On a Mac, I do this by creating a new (wi-fi) interface (called Cats), in my Network preferences, and setting its DNS server to <code class="language-plaintext highlighter-rouge">127.0.0.1</code>. You could do this on your normal interface, but this makes switching back and forth easier.</p>
<h2 id="warnings">Warnings</h2>
<p>While you’re playing with this, pretty much nothing on your computer that requires the internet works. Except for your browser. And then that’s mostly cats. So being able to deactivate this easily is kind of key (I know. You might think “Why would I ever want to deactivate cats?”, but trust me on this one). I also recommend killing all the things that need to call the mothership (google hangouts, twitter feeds, dropbox, iMessage), because they will not like your sassy cat answers, and will slow everything down.</p>
<h2 id="you-are-ready">You are ready</h2>
<p>Go in your browser to <code class="language-plaintext highlighter-rouge">www.google.com</code> and wait a bit. You should see a cat. Go to a different website. Another cat. Congratulations. Your internet is now all cats.</p>
<h2 id="wait-what">Wait, what?</h2>
<p>Do not panic. While I recommend you don’t look at the source because it’s gross, if you do look at the source, you’ll notice all it does is resolve any hostname to <code class="language-plaintext highlighter-rouge">54.197.244.191</code>, which is a magical place on the internet that has cats. My friend <a href="https://github.com/cpatulea/cats">@eigma</a> made it, and is hosting it, so please try not to kill all the cat bandwith at once. You could also resolve everything to localhost, and serve your own for now cats on an http server on port 80. But then you’d have to store your own cats locally, and that is animal cruelty. Thankfully, for now, while that magical static IP exists, you don’t have to.
That’s it, that’s all.</p>
<h2 id="i-need-to-know-more">I NEED TO KNOW MORE</h2>
<p>Here’s the little <a href="http://notwaldorf.github.io/posts/oops-cat-dns/">summary</a> I wrote originally about how DNS servers work. Basically, cat-dns ends up doing this:</p>
<ul>
<li>gruesomely parse the query from the client. I used <a href="http://www.zytrax.com/books/dns/ch15/">this</a> as a reference on what each of the fields in the message sections are, because the spec itself is very dry. This was the worst part, because the message sections are sequences of bits that don’t add up to bytes on any sane boundary, so you have to work with bit arrays, which is nobody’s idea of fun. Anyway, a spec is a spec.</li>
<li>assemble the DNS answer. The answer is mostly the same for each query – the only thing that changes is the content of the query (i.e. the hostname you requested). And you copy that from the query, so it’s not a big deal.</li>
<li>always returns <code class="language-plaintext highlighter-rouge">54.197.244.191</code> as the resolved IP, unless you’re requesting <code class="language-plaintext highlighter-rouge">imgur.com</code>. Then it gives you an actual IP for imgur that I got from <code class="language-plaintext highlighter-rouge">nslookup</code>. Imgur stores our cats, so we need to be able to get to them. :)</li>
</ul>
Dear sir or madam: the bookmarklet you didn't know you needed2014-02-11T00:00:00+00:00https://meowni.ca/posts/dear-sir-or-madam<p>Do you sometimes feel the internet is holding you hostage? Don’t you wish the internet would <em>look</em> like it’s holding you hostage? Worry no more! <a href="https://github.com/notwaldorf/dear-sir-or-madam">Dear-sir-or-madam</a> is a bookmarklet that makes web pages look like they’re ransom notes. For example, like this:</p>
<p><img src="http://i.imgur.com/Hbcj9jE.png" alt="screenshot" /></p>
<h2 id="how-to-use">How to use</h2>
<p>Bookmark this by dragging it to your bookmark bar: <a href="javascript:var i,s,ss=['//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js','//rawgit.com/notwaldorf/dear-sir-or-madam/master/ransom-it.js'];for(i=0;i!=ss.length;i++){s=document.createElement('script');s.src=ss[i];document.body.appendChild(s);}void(0);">ransomify!</a>.
Then go to a non-https webpage, and hit that bookmark. Then, wait a bit. Then, BAM. Ransomified.</p>
<h2 id="disclaimers-disclaimers">Disclaimers? Disclaimers!</h2>
<p>This doesn’t work with https websites at the moment (or possibly forever). Also, I wrote most of it in bed, at 7am, after insufficient levels of caffeine, so you can count on this being top drawer code. It’s not the fastest it can be, but it’s definitely not the n^2 abomination I wrote in the first iteration either.</p>
Oops, I accidentally the whole DNS2014-02-06T00:00:00+00:00https://meowni.ca/posts/oops-cat-dns<p>Here is my confession, internet: I am writing a cat DNS. That is, a DNS server that resolves everything to cats. You want your email? Cat! You want to check the weather? Cat! It’s always cat.</p>
<h2 id="wait-why">Wait why?</h2>
<p>We were talking at work about DNSes, and it turns out I only <em>hand wavingly</em> know how they work. I also like things that troll you. The reason why this post is about what I’m doing and not about what I’ve done, is because my server isn’t done yet.
Let me try to explain.</p>
<ul>
<li>The DNS <a href="http://tools.ietf.org/html/rfc1035">spec</a> is ridiculous. I genuinely don’t want to parse it; It’s got a billion fields, it’s written in Courier New, and it is really boring. Pros: I have found a nodejs project on github that I’m working from. Cons: it doesn’t work for me.</li>
<li>Testing the DNS server is ridiculous. You take your DNS server for granted, dear reader. I know this, because the moment I set my crappy, barely-running, returning-nothing service as my DNS, a hundred thousand requests started coming in. You see, every service in the universe (gmail, hangouts, apple notifications, twitter) polls their mothership every second to check for updates. All these polls come to your server. All you want is to try to go to <code class="language-plaintext highlighter-rouge">www.google.com</code> in a tab and see what happens. It meeps, that’s what happens. MEEP.</li>
</ul>
<h2 id="dns-how-do-it-do-it">DNS: How do it do it?</h2>
<p>You know how DNS works. You give a server a human readable hostname, like <code class="language-plaintext highlighter-rouge">www.google.com</code>, and it gives you back the IP address (like <code class="language-plaintext highlighter-rouge">74.125.226.113</code>) where the thing you are looking for actually lives. Here’s pretty much how it goes.</p>
<ul>
<li>You type in <code class="language-plaintext highlighter-rouge">www.google.com</code>. We’re off to the races!</li>
<li>This goes to a <em>recursive caching name server</em>, which, after doing some work, will give you the IP you need. This name server has a list of hints, such as addresses of <em>root</em> name serves, and most likely a cache of popular requests. Let’s say it doesn’t have an answer cached, which means it will go ask a <em>root</em> nameserver.</li>
<li>A <em>root</em> nameserver might not know the IP of your service, but knows the IP of the top level domain you need to speak to (in this case, the .com one). It also responds with ‘I don’t know, but I bet you this other IP does’</li>
<li>The top level domain server (e.g. the .com one) knows you want something about <code class="language-plaintext highlighter-rouge">google.com</code>, so it will tell you where the google <em>authoritative</em> name server is.</li>
<li>The <em>authoritative</em> name server is the best. It knows things without having to ask anyone else. The google authoritative name server is going to report back with the IP you want. Bingo bango, sugar in the gas tank.</li>
<li>The caveat here is that you have to ask it only about things it knows about. If you end up asking a google authoritative server about <code class="language-plaintext highlighter-rouge">notwaldorf.github.com</code>, it’s most likely going to apologize politely and tell you it doesn’t know.</li>
<li>Addendum: @pphaneuf says this is technically incorrect (which is the best kind of incorrect) because all non recursive servers are authoritative about <em>something</em>. The <code class="language-plaintext highlighter-rouge">.com</code> one is authoritative about who you need to talk to when you want <code class="language-plaintext highlighter-rouge">*.com</code>. @pphaneuf also writes DNSes for a living though, so his level of knowledge is over 9000.</li>
</ul>
<p>Finally, this is mostly a lie, as in real life all of these <em>recursive</em> servers that you hit first do a lot of caching. Imagine doing this 4+ step dance every time someone typed <code class="language-plaintext highlighter-rouge">www.google.com</code> in their browser. IMAGINE.</p>
<p>Cat DNS is technically a <em>recursive caching</em> name server (because it’s the first one you hit), a <em>root</em> one, and an <em>authoritative</em> one, mostly authoritative about cats. Cat DNS knows everything: it’s cats. Cats, cats, cats. This also means it should be super simple to implement.</p>
<h2 id="some-code-deets">Some code deets</h2>
<p>Things communicate with DNS servers over UDP on port 53. A couple of things:</p>
<ul>
<li>53 is a small number for a port, which means it’s privileged. Sudo that with care.</li>
<li>You talk to these servers over UDP. UDP is great because it doesn’t have a state, doesn’t do any handshakes, offers no guarantees, and is used for sending a small chunk of data. You know what’s small? The IP of a cat.</li>
</ul>
<h2 id="so">So</h2>
<p>Tune in next week for results on how this actually worked in practice, once I actually get around to writing dem codes.</p>
Cocoa gems or: how this isn't about reimplementing Ruby in Objective-C2014-01-30T00:00:00+00:00https://meowni.ca/posts/cocoa-gems<p>Because that would be crazy. Crazy is in the next blog post.</p>
<p>I’ve had to write a sizeable chunk of (fairly mediocre) Objective-C code recently, and I’ve formed the following opinions:</p>
<ul>
<li>It’s easier if you just get over the thing with the brackets</li>
<li>Event listeners are sooper cool</li>
<li>Standard Cocoa controls are great if you want them to look exactly like Apple wants them to look like</li>
<li>If you disagree with the above point, you’re going to have to play subclass bingo</li>
</ul>
<h2 id="subclass-bingo">Subclass bingo</h2>
<p>You’re a subclass bingo winner when you’ve made a custom class out of all of the NSControls. If this sounds ridiculous, it just means you haven’t tried hard enough.</p>
<p>I started playing subclass bingo at the same time I started mumbling Cocoa, which was two months ago; I relied on the internet a lot for help. Sometimes the internet let me down, as it is wont to do, and then I had to ask actual humans things that in retrospect were fairly trivial. To save you from bringing a pox on both your houses, here are three (3) custom controls that you might one day look for.</p>
<p>All of them live in the <a href="http://www.chromium.org/Home">Chromium</a> code zoo now. Token feeding and photography sessions are held three times a day, weather permitting.</p>
<h2 id="nsbutton-with-custom-padding">NSButton with custom padding</h2>
<p>By default, if you have an NSButton that has an image and a title, these will be squished right next to each other. This doesn’t always look very pretty. By default, we get the thing on the left. We want the thing on the right.</p>
<p><img src="/images/2014-01-30-button-padding.png" alt="NSButton with padding" /></p>
<p>The way we’re going to fix this is by creating a custom <code class="language-plaintext highlighter-rouge">NSButtonCell</code>, and overriding its <code class="language-plaintext highlighter-rouge">-drawTitle</code> method (I actually mean <code class="language-plaintext highlighter-rouge">-drawTitle:withFrame:inView:</code>, but I’m going to keep dropping the other parameters to make things look less scary. You can find everything in the <a href="https://developer.apple.com/library/mac/documentation/cocoa/reference/applicationkit/classes/NSButtonCell_Class/Reference/Reference.html">docs</a>, which are quite lovely).</p>
<p>If you also want to give your button a left margin (I did. I wanted that), you can also override <code class="language-plaintext highlighter-rouge">-drawImage</code> and add some spacing in there. The only thing you need to keep in mind is that because you’re adding all this spacing to the cell, you’ll need to manually update <code class="language-plaintext highlighter-rouge">-cellSize</code>, so that the correct value gets returned and your title isn’t cut off.</p>
<p>The full implementation is <a href="https://code.google.com/p/chromium/codesearch#chromium/src/chrome/browser/ui/cocoa/browser/profile_chooser_controller.mm&l=345">here</a>, and its use is <a href="https://code.google.com/p/chromium/codesearch#chromium/src/chrome/browser/ui/cocoa/browser/profile_chooser_controller.mm&l=402">here</a>. The important bits are:</p>
<figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (NSRect)drawTitle:(NSAttributedString*)title
withFrame:(NSRect)frame
inView:(NSView*)controlView {
// This is the text's origin, which is from the left margin of the button.
// If you add a left margin in -drawImage, you have to add it here as well.
frame.origin.x += spacing_;
return [super drawTitle:title withFrame:frame inView:controlView];
}
- (NSSize)cellSize {
NSSize buttonSize = [super cellSize];
buttonSize.width += spacing_;
return buttonSize;
}</code></pre></figure>
<h2 id="nsbutton-with-a-transparent-background-color">NSButton with a transparent background color</h2>
<p>Setting a normal, opaque background on a button is easy. You can do something like <code class="language-plaintext highlighter-rouge">[[button cell] setBackgroundColor:[NSColor blueColor]]</code>, however this only works for borderless buttons and opaque backgrounds. If we want to draw a transparent background, we have to take drawing into our own hands and override <code class="language-plaintext highlighter-rouge">-drawRect</code>. Custom painting? You’re well on your way to a subclass bingo! Keep in mind this isn’t the cheapest operation (it gets called literally all the time), so don’t get too ambitious in there.</p>
<p>The full implementation is <a href="https://code.google.com/p/chromium/codesearch#chromium/src/chrome/browser/ui/cocoa/browser/profile_chooser_controller.mm&l=150">here</a>, but the main method is:</p>
<figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void)drawRect:(NSRect)dirtyRect {
NSColor* backgroundColor = [NSColor colorWithCalibratedWhite:0 alpha:0.1f];
[backgroundColor setFill];
// P.S. NSRectFill(...) won't work, and will ignore the alpha. I tried.
NSRectFillUsingOperation(dirtyRect, NSCompositeSourceAtop);
[super drawRect:dirtyRect];
}</code></pre></figure>
<p>Bonus points to Cocoa for using the word “atop”.</p>
<h2 id="otter-intermission">Otter intermission</h2>
<p>I bet you feel pretty pleased with how you’re doing in subclass bingo right now. Here’s a gif of an otter who probably just subclassed a slider.
<img src="http://i.imgur.com/nUIe0yQ.gif" alt="otter" /></p>
<h2 id="nsbutton-that-changes-its-background-on-hover">NSButton that changes its background on hover</h2>
<p>Disclaimer: in Chromium, using a raw <code class="language-plaintext highlighter-rouge">NSTrackingArea</code> is a pretty big <a href="http://www.chromium.org/developers/coding-style/cocoa-dos-and-donts">don’t</a>, because it’s leaky and leads to weird crashes. We also don’t tend to use raw pointers like the code below either, because ain’t nobody got time for segfaults. Instead, we use <a href="https://code.google.com/p/chromium/codesearch#chromium/src/base/mac/scoped_nsobject.h">scoped_nsobjects</a>, which are the badass Objective-C flavours of scoped_ptrs. Refcounting 4 lyfe <3.</p>
<p>The code as used in Chromium is <a href="https://code.google.com/p/chromium/codesearch#chromium/src/chrome/browser/ui/cocoa/browser/profile_chooser_controller.mm&l=392">here</a>. I’m going to make the crazy assumption that you, dear reader, aren’t using this in Chromium, so below is a regular-world variant. I can tell you that it compiles and runs, but I am not ready at this point to make any guarantees about the irregularities in the space-time continuum it might cause. Worst case, you’ll have to release that <code class="language-plaintext highlighter-rouge">NSTrackingArea</code> when you’re done with it (e.g. in the button’s <code class="language-plaintext highlighter-rouge">-dealloc</code>).</p>
<figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">@interface HoverBackgroundButton : NSButton
@end
@implementation HoverBackgroundButton
- (id)initWithFrame:(NSRect)frameRect {
if ((self = [super initWithFrame:frameRect])) {
[self setBordered:NO];
// Bonus code for you. NSMomentaryChangeButton means that the pressed
// style of the button is the same as the active one.
// Also, look: font change!
[self setFont:[NSFont labelFontOfSize:14]];
[self setButtonType:NSMomentaryChangeButton];
[[self cell] setBackgroundColor:[NSColor whiteColor]];
[self sizeToFit]; // <--- We need this so that [self bounds] is a thing.
// Add a tracking area so that we can show/hide the button when hovering.
NSTrackingArea* trackingArea = [[NSTrackingArea alloc]
initWithRect:[self bounds]
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways
owner:self userInfo:nil];
[self addTrackingArea:trackingArea];
}
return self;
}
- (void)mouseEntered:(NSEvent*)event {
// Boom.
[[self cell] setBackgroundColor:[NSColor blueColor]];
}
- (void)mouseExited:(NSEvent*)event {
[[self cell] setBackgroundColor:[NSColor whiteColor]];
}
@end</code></pre></figure>
<h2 id="the-end">The end</h2>
<p>You’ve made it. Congratulations! Please let me know if/when you win at subclass bingo (though it’s unclear there are any winners), and I will send you another otter gif.</p>
(Potentially) neat C++ protipz2014-01-20T00:00:00+00:00https://meowni.ca/posts/protipz<p>Disclaimer: these aren’t new protipz. I didn’t make them up. They’re actually straight out of the Chromium code <a href="http://www.chromium.org/developers/coding-style">style</a>, they’re pretty trivial, and you might already use them. But just in case you’re not a Chromium committer (the outrage), or are fairly new at C++ and want to make your code less suck, here they are. I think they’re neat.</p>
<h2 id="copy-constructors-and-their-brethren">Copy constructors and their brethren</h2>
<p>You know that scene from The Fly when Jeff Goldblum, having not screwed up teleporting a small baboon, decides he
should totally teleport himself? But then he screws that up (because software), manages to turn himself into a giant terrifying fly (because David Cronenberg), and continues to give me nightmares as an adult.</p>
<p>That’s exactly how I feel about copy constructors. You can absolutely get them right, but they’re a <a href="http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Copy_Constructors#Copy_Constructors">pain</a>, and among other crimes they’re committing, they’re sometimes deceivingly slow. The point is, most of the time you don’t even need them. I mean, Jeff Goldblum teleported himself like three meters away. Couldn’t he have just walked?</p>
<p>What we tend to do instead is convince the compiler to get annoyed with us if we try to use a copy constructor. This is easy because the compiler <3s being annoyed with us. So we can <a href="https://code.google.com/p/chromium/codesearch#chromium/src/base/macros.h&l=28">define</a> a nice macro (stay with me) that adds a private declaration, but doesn’t implement it:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="c1">#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
</span><span class="n">TypeName</span><span class="p">(</span><span class="n">const</span> <span class="n">TypeName</span><span class="o">&</span><span class="p">);</span> \
<span class="n">void</span> <span class="n">operator</span><span class="o">=</span><span class="p">(</span><span class="n">const</span> <span class="n">TypeName</span><span class="o">&</span><span class="p">)</span></code></pre></figure>
<p>Which you would then use in your private section of your class, like so:</p>
<figure class="highlight"><pre><code class="language-c--" data-lang="c++"><span class="k">class</span> <span class="nc">Hooman</span> <span class="p">{</span>
<span class="nl">public:</span>
<span class="n">Hooman</span><span class="p">();</span>
<span class="o">~</span><span class="n">Hooman</span><span class="p">();</span>
<span class="nl">private:</span>
<span class="n">DISALLOW_COPY_AND_ASSIGN</span><span class="p">(</span><span class="n">Hooman</span><span class="p">);</span>
<span class="p">};</span></code></pre></figure>
<p>Now, when you try to be ambitious and clone Jeff Goldblum,</p>
<figure class="highlight"><pre><code class="language-c--" data-lang="c++"><span class="n">Hooman</span> <span class="n">jeffGoldblum</span><span class="p">;</span>
<span class="n">Hooman</span> <span class="nf">teleportedJeffGoldblum</span><span class="p">(</span><span class="n">jeffGoldblum</span><span class="p">);</span></code></pre></figure>
<p>Clang will tell you something like “error: calling a private constructor of class ‘Hooman’”. Other compilers might tell you other things, but they’ll generally have the same annoyed tone. Now would be a good time to apologize to your compiler for all the silly things you’ve done in the past.</p>
<h2 id="digression-on-macros">Digression on macros</h2>
<p>My C++ motto is “Yes, but just don’t”. Yes, macros are weird and evil and if you use them incorrectly you will open the hellmouth. So try not to. We will use macros in a civilized way in here, and if you’re writing something like <code class="language-plaintext highlighter-rouge">#define TRUE FALSE</code> we will all agree that it was an uncivilized thing to do and it’s your turn to tell Buffy about the hellmouth.</p>
<h2 id="debug-checks">Debug checks</h2>
<p>The Chromium code is peppered with these things we call <a href="https://code.google.com/p/chromium/codesearch#chromium/src/base/logging.h&l=734">DCHECKs</a>. They’re asserts that run only in debug builds, so that you will catch bad scenarios in development and testing but you won’t give the user a panic attack in production. Ideally, we all have 100% test coverage that we run in debug mode (because it’s obviously sooper fast), so we detect all of the herp derps and nothing ever goes wrong in production. Ideally.</p>
<p>My favourite usage of dchecks is to make sure that I’m not accidentally breaking code by adding new values to an enum, and have them be unintentionally handled by catch-all blocks.</p>
<p>Let’s pretend we have this enum:</p>
<figure class="highlight"><pre><code class="language-c--" data-lang="c++"><span class="k">enum</span> <span class="n">THINGS_TO_WEAR</span> <span class="p">{</span>
<span class="n">SOCKS</span><span class="p">,</span>
<span class="n">HAT</span>
<span class="p">};</span></code></pre></figure>
<p>Which we would use for dressing up in the morning like so:</p>
<figure class="highlight"><pre><code class="language-c--" data-lang="c++"><span class="k">if</span> <span class="p">(</span><span class="n">thing</span> <span class="o">==</span> <span class="n">SOCKS</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Put on feet.</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">DCHECK</span><span class="p">(</span><span class="n">thing</span> <span class="o">==</span> <span class="n">HAT</span><span class="p">);</span> <span class="c1">// <-- The important bit.</span>
<span class="c1">// Put on head.</span>
<span class="p">}</span></code></pre></figure>
<p>This way, if later on someone adds <code class="language-plaintext highlighter-rouge">PANTS</code> to <code class="language-plaintext highlighter-rouge">THINGS_TO_WEAR</code> and ends up calling this code with <code class="language-plaintext highlighter-rouge">thing = PANTS</code>, the runtime will meep and I won’t have accidentally put my pants on my head. See what I did there? (If you were bothered that the enum wasn’t sorted alphabetically, now you know why. Let’s move on.)</p>
<p>You can also do this with a similar <a href="https://code.google.com/p/chromium/codesearch#chromium/src/base/logging.h&l=783">NOTREACHED()</a> assert, to make sure your new values are not caught by a default switch case.</p>
<figure class="highlight"><pre><code class="language-c--" data-lang="c++"><span class="k">switch</span> <span class="p">(</span><span class="n">thing</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">SOCKS</span><span class="p">:</span>
<span class="c1">// Put on feet.</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="n">HAT</span><span class="p">:</span>
<span class="c1">// Put on head.</span>
<span class="k">break</span><span class="p">;</span>
<span class="nl">default:</span>
<span class="n">NOTREACHED</span><span class="p">();</span> <span class="c1">// MEEP.</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>
<h2 id="unnamed-namespaces">Unnamed namespaces</h2>
<p>This is an unnamed namespace, which I am declaring in a <code class="language-plaintext highlighter-rouge">.cc</code> file, and it is the coolest:</p>
<figure class="highlight"><pre><code class="language-c--" data-lang="c++"><span class="k">namespace</span> <span class="p">{</span>
<span class="kt">bool</span> <span class="n">MyAmazingHelperFunction</span><span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>Reasons why it’s the coolest are:</p>
<ul>
<li>This function is available only inside this <code class="language-plaintext highlighter-rouge">.cc</code> file so it doesn’t make your class obese.</li>
<li>You don’t have to remember which of the 3+ meanings of <code class="language-plaintext highlighter-rouge">static</code> you’re referring to when defining a file scoped static variable. This means you’re playing by the “Yes, but don’t” rules of having fun with C++, which don’t give you headaches.</li>
<li>If you care about this sort of thing, your function name gets a nicer mangled name.</li>
</ul>
<p>It’s basically just that second bullet though.</p>
<h2 id="forward-declarations--includes">Forward declarations >> #includes</h2>
<p>This is probaby the most boring of all the topics, but the most useful one. Having a header file include everything but the kitchen sink is a little unfortunate. Your compiler is unhappy because it needs to open all those files, which in turn will make you unhappy, because every time you touch a header file, it will trigger seventy billion other files to feel like they need to be recompiled. Ain’t nobody got time for that.</p>
<p>Instead, what we can do is forward declare the class (<code class="language-plaintext highlighter-rouge">class Foo;</code>) in the <code class="language-plaintext highlighter-rouge">.h</code> file, and include it (<code class="language-plaintext highlighter-rouge">#include "Foo.h";</code>) in the <code class="language-plaintext highlighter-rouge">.cc</code> file. That basically means you’re promising the compiler this type exists, and that you will tell it what the type looks like when it (the compiler) needs it. If the compiler needs to use the type and you haven’t included it, I promise you it will meep.</p>
<p>But because this is C++, the rules of this game are a little tricky, and will sometimes get you into an argument with the compiler. The question I try to answer is “Does the compiler need to know the size or contents of the class <code class="language-plaintext highlighter-rouge">Foo</code>?”</p>
<ul>
<li>If the answer is yes, and the compiler cares (e.g. inheriting from/making a member of that incomplete type), then you won’t be able to forward declare it. You have to do the promising and the explaining in the same place, so might as well just include the file.</li>
<li>If the answer is no, and the compiler doesn’t care (e.g. you’re declaring but not defining functions that use the incomplete type), then forward declare it away!</li>
</ul>
<p>This means you have to be a little careful when including the type in the <code class="language-plaintext highlighter-rouge">.cc</code> file. If there are two types that are called the exact same thing and you include the wrong header, you’ll have a bad time. So, you know, just don’t. :)</p>
<h2 id="thats-it-thats-all">That’s it, that’s all.</h2>
<p>Go forth and write nicer C++ code.</p>