Friday, June 5, 2020

running your own npm repository

Verdaccio seems pretty good to set up your own local npm repository, like a poor man's artifactory (guess these all have passthroughs to the main npm).  Here's a decent walkthrough for a hello, world.

Thursday, June 4, 2020

great twitter thread and article on how few coders actually remember everything these days

If You Want to Be a Senior Developer, Stop Focusing on Syntax has a delightful twitter thread starting
Hello, my name is Tim. I'm a lead at Google with over 30 years coding experience and I need to look up how to get length of a python string. --@tdierks
That's really the way of the world these days.

Tuesday, June 2, 2020

challenges prioritizing specific classes using sass themes and css modules

At work we're proof-of-concepting using Sass and CSS modules, trying to find easy, flexible patterns to allow projects to have their own themes while still having shared components carry their own CSS.

Things built up pretty well: we had a project that had its own .scss file, and then a subproject with a .scss file that imported the base .scss.

I made it so our shared components could take in a className property, so that individual instances of a shared component can use a different class - or rather, can use that class as well. That worked ok for the main project, but the subproject's defaults were being applied after the individual override class.

I thought of insisting that override classes be "inside" of the class their overriding, like:
.btn {
   .specialcaseclassname {

But that wouldn't work for the subproject including the base projects "btn" (but overriding it) - the name were munged

Three possible solutions:

  1. !important would work, but everyone would hate that. 
  2. We could set up a rule that every override class in .scss would have to use @extend against the base class, and then change our components so it would leave out the theme's class.
  3. We can declare that all override classes (at least those that are shred by multiple subprojects) must be in a separate file (and the override Sass files must be imported after the main theme Sass)
It's really a bummer that the order CSS classes are specified in for a DOM element don't matter,
<div class="mainthing specialcase">
is the same as 
<div class="specialcase mainthing">

Otherwise we might have another option. But I guess "3" it is.

Sunday, May 31, 2020

adding git branch info to macos zsh prompt, and using pico

I'm trying to get a more fluent with command-line git, and it's pretty easy to get zsh to show you the current git branch as your prompt. Here's my new ~/.zlogin:

alias ls="ls -F"

autoload -Uz vcs_info
precmd() { vcs_info }

# Format the vcs_info_msg_0_ variable
zstyle ':vcs_info:git:*' formats ' 🛰 %b'

# Set up the prompt (with git branch name)
PROMPT='${PWD/#$HOME/~}${vcs_info_msg_0_} 🚀'
I like using emoji as concise and obvious at a glance prompt delimiters.

Also I have to be sure to set a default editor I am comfortable with:
git config --global core.editor "nano"
Interesting that emacs doesn't ship with MacOS, but I'm not so enamored of it that I'm going to seek it out. Never got my mind around the modality of vim. nano/pico feel kind of clunky and almost DOS-ish, but for the small number of tasks where it's not worth firing up Visual Studio Code, it's fine.

Saturday, May 30, 2020

for people considering a career in ux

I was contacted on LinkedIn by a person thinking of doing a UX Designer bootcamp. And I have another friend going down the same route.

I know it's easy to think of the career choice as "UI for people who are too intimidated by front end programming" but that's not fair - there are a lot of skills a good fulltime UX designer has that I only have in a rudimentary form (my current job title of "UX Engineer" not withstanding... I do fear they are using the aspirational sense of UX vs UI vs the in the trenches one.)

My partner M. got a bit frustrated with her role as UX Researcher.  She finds Nobody told me UX would be like this to be pretty accurate. It's not meant to scare people off, but to have realistic expectations - many places don't embrace UX expertise as much as they should, since often it can run counters to the "gut knowledge" of developers and business planners.

A Day In The Life Of A UX Designer is a bit more sanguine.

Also M says if you're in Boston, check out some of the meetups (not sure if they are still operating in quarantine times in a virtual form or what) - UXPA BostonBostonCHI, and Ladies That UX Boston.

Friday, May 29, 2020

combining css modules, sass, and context providers for swappable themes with locally scoped css

Esay Silva's How to use Sass and CSS Modules with create-react-app does a great job of setting up Sass with create-react-app and then using them as CSS modules... node-sass does a good job of letting your create-react-app created react app use Sass - if you name a css file Foo.module.css or Foo.module.scss, it gets automagically locally scoped classnames, so your react code can be like

import BrandStyle from "./GreenCo/Theme.module.scss";

and then later 

<button className = {BrandStyle.btn}>Assuming .btn was the original class name</button>
The css sent to the browser will prefix .btn with a unique bit of randomness, so you can safely have lots of components with css code that refers to "btn" and they won't interfere with each other in the browser.

There's nothing too magic about that object you get from the import (the one I'm calling BrandStyle here) - it's just a map from the name as it appears in your original .scss or .css to the name it shows up as in the css that is sent to the browser. (And you can further refer to multiple class names, either with the lightweight classnames utility or just by gluing various parts together with spaces.)

But if you're thinking in terms of a customizable Component Library where you would like developers to be able to override styles of some basic components, but without passing in the overridden styles or CSS to every child, you can do it via context.

I was  rusty on React Contexts - at first I thought by wrapping a segment of JSX/Dom in a Context, each child would get new properties automagically... nope! "Context" is its own beast and doesn't use props to do its thing.

It seems best to make up a trivial custom Context object that can be referenced both by the parent / grandparent app or component as well as the children that will be reading from it...

import React from "react";
const ThemeContext = React.createContext();
export default ThemeContext;

and then use it as a Provider in the parent:

import BrandStyle from "./GreenCo/Theme.module.scss";
import ThemeContext from "./core-ux/ThemeContext.js";
      <ThemeContext.Provider value={{ brandstyle: BrandStyle }}>
          <PushButton>Button by Theme....</PushButton>

and then refer to it in the child:

import styles from "./PushButton.module.scss";

import ThemeContext from "./ThemeContext.js";

const PushButton = (props) => {
  const theme = React.useContext(ThemeContext);

  const comboStyle = `${styles.btn} ${theme.brandstyle.btn}`;

  return (

So here we see how it can have its own local styles in PushButton.module.scss, grab an additional style from the ThemeContext, and then combine them in a string. (In the actual code I do more checking to make sure theme.brandstyle exists.)

making a new mac feel like home

When I replaced my 2012-era MacBook Air with one of the new models I used Apple's Migration Assistant - good results in the end.

With my new work laptop (mercifully one of the new MacBook Pros with a proper escape key) I was setting it up from scratch, so here were some notes on the quality of life improvements I make, most of which I've written about before:

Settings I fix up:
  • Add a "Quick Action" for convert to jpg (for HEIC images bumped over from iPhone)
  • Make Paste and Match Style the system wide default for cmd-V - especially noticeable for the Stickies application
  • Change the default Screenshot location to ~/Downloads
  • Arrange the Finder sidebar - I do favor ~/Downloads as my generic "temp" space so it makes sense to make it higher in the shortcut list there.
  • Get all that extra crap out of the Dock and make it automatically hide
  • Make Terminal automatically close when I exit the shell ( Go to Terminal|Preferences|Profiles|Basic/Default and select “When the shell exits:”)
  • Finder I set to always show extensions
  • I don't like most of the "More Gesutres" under Trackpad settings except for Mission Control (3 finger swipe up to see all windows) and I enable App Exposé (3 finger swipe down for just the windows of the current app - not sure why that's disabled by default.)
  • MacOS' default shell is now zsh. I fire up `pico ~/.zlogin` and set the following:
    alias ls="ls -F"
    That shows me which directory entries are directories themselves, and then a nice minimalist prompt with a condensed version of the path, and the rocketship (from Edit | Emoji & Symbols) reminds me I'm on my home system.
Some Apps I add:
  • SizeUp and HyperSwitch - I set up opt-cmd-arrows to bounce around windows like a boss with the keyboard in SizeUp, and HyperSwitch restores sanity to switching to Terminal or Finder - opening a window if there aren't any there, just like clicking on the Dock.  
  • Add the convenient menu bar status menu quick note apps Tot and Tyke - (also for the first time I noticed I can use cmd-drag to rearrange that top bar! My employer has a bunch of VPN and file sharing stuff that has to live there but I can shunt it off to the side)
  • Add Haptic Touch Bar, switch over to traditional button like controls, and shove them over to the side where they won't be accidentally brushed... (still so foolish that a finger brush is the same as a click for the touch bar!)
Common Apps I need to tweak:
  • In Chrome I like the "Humble New Page Tab" for getting a view of my bookmarks, and adding the React plug in etc.
  • Install Visual Studio Code... I like the “Quiet Light” Color Theme, and then I've been using plugins Prettier - Code Formatter (making sure format on save is on), ESLint, ES7 React/Redux-Native snippets, Babel JavaScript - and I disable auto-completion of quotes and braces

Anyway, nice to have reference to the little things that make a Mac more comfortable for me.

Sunday, May 24, 2020

interesting post roundup

After this last round of job interviews, I want to be paying more attention to trade articles and what not. And it seems to make sense to post them here, even though part of me says "well shouldn't people just go to sources I got them from?" But this blog can both A. serve as a curation for other people (honestly there's a lot of stuff especially on Medium that feels a little questionable) and B. this blog has always been notes for my future and present self - future self for the obvious "in case I think of it and want to find it" and present in the sense that maybe making a link will help me remember the article in the first place.

So I'll stop apologizing for blogging, and get on with it...

Stop Writing Reusable React Components has a great point, that pre-engineering for reusability is often counter-productive. (It reminds me a bit of my anti-"buy" lean in make vs buy; sometimes using a toolkit just for one part of its featureset is like buying a whole house because you loved the modern kitchen.) Also, I'd say if the choice comes between "reusable but complex enough it well-justifies its unit tests" vs "a one-off simple enough that there's not much that can go wrong", i.e. few moving parts, choose the latter. (And amusing third hand line from the article:  To steal a line from Chantastic over at React Podcast, my code was “so DRY it chaffed.”) (here's sort of a counter-argument, or maybe a case in point; what are components doing where it would be worth handling overrides vs letting people make their own bespoke copy?)

Why You Should Be Storing Remote Data in a Cache (and Not in State) seems a pretty good answer to a question I've been thinking about, where to put data if you're not using a central store ala Redux or MobX... this pattern basically has a key of the ajax request, caches the result, and then has components rerender if the value returned from the server is different.

Learning a bit about tagged templates in this piece on styled components - boy, I am not sure "tagged templates" pull their weight - yet another damn bit of syntactic sugar to learn, with oddly crude implementations - but maybe I just haven't seen their sweet spot yet.

Other things: Verdaccio is an easy way of running a local npm repository.

How to use Sass and CSS Modules with create-react-app is just what it says on the tin - great when you want to have safe and modular CSS via Sass.

Saturday, May 23, 2020

pretty good video on js most used tricks

I think I'm due for thinking more about querySelector() as well as asynch/await all that goodness.

Also I kind of like the thinking behind this custom hook to manage UI state as status - starting with the UI trope of "loading w/ spinner, then show results, or empty, or error"

Wednesday, May 20, 2020

second-guessing the modern web

Apparently making the rounds, Tom MacWright's Second-guessing the modern web.

I've been talking with many folks doing my job interviewing (mercifully drawing to an end) as well as doing a small pile of mini-projects, and the whole question of what makes a good back end and what makes a good frontend is very much on my mind, and I appreciate this semi-contrarian view.

I wonder if I'm a little insensitive to render performance, or if some people are too uptight about it. Even the level of React's Virtual DOM - my new online card game does something like declarative rendering, but just raw, and for smallish updates there's no sense of flicker, even though the whole UI is being erased and built back up.

(See also Rich Harris' rebuttal)

Tuesday, May 19, 2020

some useful react libraries

5 Awesome React.js Libraries You Should Know About. The toaster popup especially seems useful. Also this one on React Animation Libraries. I still think declarative languages are less friendly towards graceful transitions in general; re-renders natively jump from snapshot to snapshot so it takes some wrangling to think of things as persistent and changing.

Monday, May 18, 2020

#foolish-idea-friends, or how to make company slack culture fun and creative

One of the things I miss most from my old job was a certain slack channel - "#foolish-idea-friends" (channel name changed to protect the guilty.)

The heart of the slack channel was an ever-growing number list of foolish ideas - concept pitches. A few basic rules were established from the outset: no criticism of ideas were permitted... just because an idea was foolish didn't mean it was "bad"... ideas had to be original as far as we knew to get a number (later a habit of bringing in ideas from elsewhere with the tag "#SEFI" (for "someone else's foolish idea") instead of a number)

One of the fellow pioneers of the channel had written bots to scrape the ideas (one Foolish Idea was to make a "best of" book) and sent me a list of my 1000+ contributions. I had done over a quarter of the almost 4,000 the channel was up to upon my departure!

FIF #600 gives an idea of what the channel was most often used for:
  • #600 categorization system for f-i-f; semi-serious but possibly outlandish proposals, less serious proposals just for a laugh, ones added quickly to grab a big number, thinly veiled complaints about stuff in the office, general-joshing on each other, other meta-stuff
So, I read through all my entries and here were my very favorites, in roughly descending order of how much I liked 'em:
  • #1500 new html6 attribute: onlick. Like onclick, but you know, for licking things. Steve Jobs said "We made the buttons on the screen look so good you'll want to lick them." and I think modern web browsers should be ready for this.
  • #321 formation of a committee to determine the plausibility of "aggressive passive" behavior; for example, furiously hammering water
  • #212 A Street Fighter-like game but with the characters from Peanuts (Snoopy, Charlie Brown, etc)
  • #1465 Trying to declutter but Marie Kondo is too pile-centric or mumbo-jumbo-y? Arrange all your possessions in a long straight line ordered by how much you want each item, make a perpendicular line at the cut off point, and discard everything to left. DONE AND DUSTED.(Note, you may still have to dust, especially around those shelves where the cluttering items used to sit.)
  • #1576 If you have a device or thing in your life you really like and use a lot, like a laptop or iPad or video game system or something, have a loved one wrap it up so you can open it christmas day and think about how much you like it.
  • #823 A service where university professors will adopt your child so they can get free tuition at that university
  • #2565 A post-mortem service to excise and preserve specific body parts for rhetorical purposes. For example, a wedding finger for a loved one to keep, or a middle finger to send to a person you despised in life, or maybe your whole butt for a hated organization to kiss. The body part can be cremated, preserved in resin, or bronzed.
  • #2226 Life in a zero-G or microgravity environment (like on an orbiting station or a spaceship that is not manuevering) offers many challenges. When you're exercising on the treadmill or just sitting at a work console, you need to strap yourself down with elastics and velcro. Exhaled CO2 silently pooling around your head is a constant threat if the air circulation system isn't perfect. And if you lose momentum in a large open area, like thanks to air resistance, it can be nearly impossible to get moving again. The solution to all of these is clear: astronauts should always wear old-school propeller beanies at all times when not in their helmets.
Some of my ideas I'd like to see for real:
  • #1368 every damn smoke and carbon monoxide detector should have a small lcd or e-ink screen that will you tell you its status. no more of this stupid "let me communicate through infuriatingly intermittent chirps". i mean seriously
  • #1474 a film crew/biography service that parents can reserve for their young kids. Every year for your unbirthday (6 months opposite your birthday) they come and do a "day in the life" taping, as well as have their writers talk to you and write up what's on your mind and what your life is like
  • #3470 make tombstones out of wet cement and let people at the funeral scratch in their final messages to the deceased
  • #887 All light switches should have a tiny LED to show if it's on or not. (In some rooms it's obvious, but when the light switch is on the other side of the closed door...)
  • #1471 an app to help remind you to keep in contact with friends you don't see regularly. Enter your friends names, and how important they are to you, and it will create a queue of people and nudge you when you should send a "just saying hi" message
  • #1588 First Class Deluxe Junk Retirement. Trying to declutter, but stuck with stuff with sentimental value, and/or with no second-hand value? First Class Deluxe Junk Retirement will come to your house to pick it up, take a photo and send it to you, and then put it in a special mausoleum landfill. For example, my "Euclid High School English Departmental Award" medal with ribbon, from 1992. I will never do ANYTHING with this, right? And yet it's weirdly hard to get rid of. Ditto my heavy duty Varsity Jacket with built in cape/hood.
  • #1438 One of my best FIFs ever - ok - so our cat has a "food puzzle" where you put kibble in a bunch of little plastic obstacles so the cat can't just chow down, but has to work a bit for each piece. It's meant for exercise, I guess, besides the mental stimulation factor, but it would be MUCH better if you could pour the kibble into some kind of little home base thing and then have little robo mice, each carrying a single platter that could hold just about one piece of kibble, and the cat could chase each bit, knock it to get the kibble, and then it would go back to the home base to get another piece...
  • #355 see-through lids for toilets so you can ensure all the material was whisked away instead of leaving it as a gross surprise for the next person, and without aerosolizing the gross stuff either. (Or possibly a sophisticated sensor system to take of reflushes automagically)
Of course, some FIFs I ended up doing for real:
  • #1073 learn that little 'waiting' step b-boy dancers do right before they launch into something truly impressive. but never do a fancy move, just do the step over and over
  • #768 Make an MP3 of silence or someone going "that's the end!" and name the tune early in the alphabet, so when iOS has decided it has run out of audio to play and for some reason starts playing "all songs alphabetically" you don't get blasted by Michael Jackson going "A B C, easy a 1 2 3" ALL THE DAMN TIME
  • #1515 out of churros? try this delicious salty, sweet, crispy, chewy dessert: take a chocolate from the front desk (preferably dark or choc w/ caramel) and put it between two tortilla chips!
  • #2758 new drink: Apple X-Cider -- take one cup of company cider, drink while sucking on an Atomic Fireball candy
Some of my thinly-veiled complaints:
  • #270 default slack avatars that are flash GIFs that trigger epileptic seizures so users have a moral and possibly legal obligation to change them
  • #391 gamify gamification! every scoring rule you invent for whatever you are gamifying gets you a G.G. Star!
  • #485 Start postfixing updated versions of functions etc with "New". This is super great for sites that have "New" and "Used" cars, so people get to enjoy making up heuristics like "if the New is a prefix, it's probably about new cars, but if New is a postfix, it's probably just a later version of a previously existing function"
Sometimes I just went for the gag or pun:
  • #2718 Luggage with face detection that is also delighted to see you as it swings around the baggage carousel and can express that- makes you feel loved after a rough flight and helps you locate your bag. The name of this wonder-product? "emotional baggage"
  • #432 pave a road with bad intentions, see where it gets you
  • #2190 market an extra-rough brand of kleenex called "grindstone"
  • #2793 football uniforms made out of really stretchy material so if a defender grabs you by the uniform you can just keep on running. Bonus: if defender is really strong and heavy and manages to hold on, comedy as you get flung back to him like a rubber band.
  • #2567 Along with surgery to reduce snoring, surgery to increase snoring but it comic ways - giant long snort on the inhale, descending whistling tone on the exhale.
  • #38 alcoholic drinks mixed with aspirin and gatorade to get a jump on your hangover the next day
  • #250 a bumper sticker that wards off cops by saying "don't worry i've driven MUCH drinker than this"
  • #2560 combine "Now & Laters" candies with "Atomic Warheads" and call them "Apocalypse Now & Laters"
  • #2714 make a 60s/70s mashup cover group called "Earth, Blood, Wind, Sweat, Fire, and Tears."
  • #711 Find that butterfly whose wing flapping is causing tornadoes on the other side of the world and STOP IT, FFS
And finally, a few I sort of liked but didn't make the other lists:
  • #235 phone cases marketed by how they feel- texture, weight, etc- more than how they look after all you are mostly just looking at the glass screen, but how they feel to your hand whether out or in your pocket is important all the time
  • #413 A Todo App that indicates items have been there too long by overlaying them with subtle cobwebs... you can even swipe them away to indicate progress on the task or just out of frustration
  • #193 conduct a social engineering campaign to change social mores so it's ok to react when people are in the bathroom stall next to you: cheering them on if they make a particularly impressive sound, sympathizing if it sounds like they're having problems, etc
  • #1055 Micro-Time-Zones. All clocks in a timezone are set to the nearest minute based on the current Longitude. No more stupid bumps when you cross certain state borders!!!
Anyway. This is the single piece of company culture I'll most miss from my old place, and I think I have a FIF in there about launching new colonies. I'd really recommend it for any big slack organization.

Oh one bonus I almost missed:
  • #1593 Make years start on March 1. This will have 3 big advantages:
    1. meteorological seasons now line up year - starting with March/April/May spring, June/July/August summer, the school year starts with Sep/Oct/Nov Fall, and then Dec/Jan/Feb Winter 2. September and October now fall on their appropriate Latin numbers (7 and 8)
    3. NFL season is no longer this weird ambiguity springing from regular seasons and playoffs of one "season" being in different years

Sunday, May 17, 2020

node/shell mashup to get stuff done

Once upon a time I used Perl for everything - shell-script type stuff as well as Web CGI.

For a while PHP became a similar go to for everything- it's a little unusual to do shell-script stuff in it, but it's pretty handy.

Now, I've grown some comfortable with JS/ECMAScript for handling arrays and filtering and what not I'd like to use it, but I'm a little lazy about looking up the syntax for the actual file operations - so I treat it as a text manipulation exercise, and go back and forth copy and pasting with a terminal window.

For example, for a big PDF conversion, I did an `ls` in Terminal (sometimes piping it to cat to make sure it was a single column, and then pasted that as a big quote in a .js file:

const raw = `some file.pdf
someo therfile.pdf
yet another_file.pdf`;

then something like

let c = 1;
const origs = raw.split('\n');
for (orig of origs) {
    console.log(`mv 'orig/${orig}' convert/${c}.pdf`);

running that in node gives me a list of shell operations I can review and tweak if needed, and then copy and paste and run.

It ain't pretty but one off tasks don't have to be :-D

Friday, May 15, 2020

safely shuffling an associative array in php

I'm making game server endpoints in PHP because it's convenient to deploy, but I'm sort of regretting it. I should really get my node mojo working...

Anyway, I thought I had a problem with json encoding / decoding because of PHP's ambiguity of regular arrays and associative arrays, because what I wanted to be a map of keys to values was coming across as a regular array in javascript land. 

After some cursing and debugging, I realized it was because I was calling "shuffle()" on the PHP array. (Leaving aside the meh-ness of hoping to get an order of a javascript map). Anyways, shuffle() doesn't respect string keys, it just drops the keys and treats it as an ordered array. Which sort of makes sense but you think there would be a warning or something...

Anyway, this function lets you shuffle the keys of an associative array (I assume without the shuffle it's usually insertion order?)

function shuffle_assoc_array($orig) {
    $shuffled = array();
    $keys = array_keys($orig);
    foreach ($keys as $key) {
    $shuffled[$key] = $orig[$key];
    return $shuffled;

Eh, good enough.

UPDATE: The above was needed for shuffling a collection of player objects (so as to not bias the judge). The main deck is a regular array... for reference here is some code that regenerates the deck (each card is a number from 1-350) after removing the cards that are already in the players hands:

#do a reshuffle if we have less than ten players per deck... 
$playercount = sizeof($game["players"]);
if(sizeof($game["deck"]) < ($playercount * 10)){
    #all cards but those currently in players' hands
    $cardsinhands = array();
    foreach($game["players"] as $player){
        $cardsinhands = array_merge($cardsinhands,$player["cards"]);
    #remake entire deck, but take out the ones in players hands...
    $deck = array();
    for($i = 1; $i <= $DECKSIZE; $i++){
    $deck = array_values(array_diff( $deck,$cardsinhands ));
    $game["deck"] = $deck;

Wednesday, May 13, 2020

little baby json server for testing react against

I had a few at-home programming tests and one of the better ones started with a simple create-react-app-style bit of boilerplate including clever us of json-server which gives you a baby server that applies REST-ful requests to a json file, all on your file system.

Reminds me a little of getput a tiny node-based server that I made 5 years ago, though I used my preferred method of separate files for the each "table row", which helps make sure any screw ups minimize damage...

react hooks, updating 'initial state' for repeated renders of a modal

Hooks really feel like the right thing for React and for small to medium-sized components they make it easy to bundle all your state right there in what is otherwise a pure functional component

The paradigm for tying in an input field to the state is pretty clean-

First you get the local variable and a way of changing it:
const [name, setName] = useState('');
Then you render an input field like this:
<input value={name} onChange={(e) => setName(} />

I ran into a gotcha with a react-modal component, one that is effectively reused across the life of the parent component. Using something like
const [name, setName] = useState(;
wouldn't reset things so to speak - name was being reset to the initial value from the first render even as the props were updated and the entire component was being re-rendered.

The solution is to combine it with the useEffect hook to get that side effect action going:
Once you've called useState, do something like
useEffect(() => {
   setName( ? : '');
}, [props]);

That let the inputs have the right initial value while still updating the local state as the user typed.

Sunday, May 10, 2020

factor of duh - reading JSON post data

Heh. So I realized one of the reasons I shanked that tech take home, besides the weirdness of JS date transform is I had a one letter type (header not headers) in the JSON fetch code I used. I really should have sucked it up and used jQuery or some such. Talk about premature optimization...

But I stumbled over that when realizing I was behind the curve in my "use PHP for JSON endpoints... to read a JSON post, you can't rely on $_POST, you need something like:
 $POSTJSON = json_decode(file_get_contents('php://input'),true);
which will then be the associative array you might expect...

php file locking for multiuser games

So I'm messing around with making quarantine-friendly version of the like-Cards-Against-Humanity-But-With-Comics game Joking Hazard. I'm thinking each game will just be a single .json file on the server, each client will be a SPA in the browser, the endpoints will be humble PHP scripts that update the file - the clients will poll and get an updated version of the whole game state, and then modify it according to the rules.

It's surprisingly tricky to teach a computer to referee even a medium complexity board or card game (It reminds me I need to go back and update Score-Matic for Dixit, another lovely boardgame, but it's casual-friendly creative/artsy mojo is marred by a fiddly scoring system).

I think I am basically making a state machine: not counting the games red cards (a slight fork) the core game loop is something like

[create-game(username)] /* put all cards in deck */
GAME_SIGNUP (add username)  <<< [join-game(username)] /* assign 7 cards from deck */
NEW_ROUND <<< next judge assigned, deck-picks-card
[judge-picks-card(cardoff, judgepos)
REG_PLAYERS_PICK <<< [reg-player-pick(cardoff)
(all players_pick)
REG_JUDGE_PICK <<< [reg-judge-pick(player)]  /*shows all picks to everyone */

So one challenge will be making sure each client can update the gamestate without stomping on other people's play- PHP supports filelocking, though you have to start using syntax more complicated than good ol' file_put_contents() - since there's a lot of boilerplate, I'm going to use a callbacks to a shared function:

function updateGame($gamename, $function){
    if(! preg_match('/^[A-Z][A-Z][A-Z][A-Z]$/',$gamename)) {
        echo "shenanigans!";
    $filename = "games/$gamename.json";
    //Open the File Stream
    $handle = fopen($filename,"r+");
    //Lock File, error if unable to lock
    if(flock($handle, LOCK_EX)) {
        $oldguts = fread($handle, filesize($filename)); //get current state
        $newguts = call_user_func($function,$oldguts); //update state
        ftruncate($handle, 0);    //Truncate the file to 0
        rewind($handle);           //Set write pointer to beginning of file
        fwrite($handle, $newguts);    //Write the new Hit Count
        flock($handle, LOCK_UN);    //Unlock File
} else {
    echo "Could not Lock File!";
//Close Stream
Heh, this is extremely FP! Get a copy of the state, modify it, replace the old state.

So my test code for a call back for this was like:
function addSomething($oldraw){
    $content = json_decode($oldraw,true);
    $content["foo"] = array();
    $content["foo"]["bar"] = "baz2";
    $content["foo"]["bat"] = "bag2";
    return json_encode($content);
It feels a little odd to pass in the function as a string, but whatevs.

rsync + fswatch for automated push to server

Like 90% of the work I do on my website, I do using a homebrew in-browser editor system (backed by PHP) I made by wrapping "Ace". It's super-convenient to be able to edit any file on any of my sites but it is ultimately limiting: I've been too reluctant to set up a build system, and so would only use technology stacks that were basically scripted, and thus the gap between my coding style at work and my coding style for fun was widening.

Baby steps would be setting up automatic file synching between my laptop and my server - plus I've really started liking VSCode's prettify module, everything just feels so professional when it's neatly reformatted...

The answer here was a good start. For my new project (an online, social-distance way of playing the excellent creative card game Joking Hazard) I ended up with a pair of alias like this:
alias joking_rsync='rsync -azP --exclude ".*/" --exclude ".*" --exclude "tmp/" /Users/LOCALUSER/data/dev/2020/jokinghazard'
alias joking_rsync_watch='joking_rsync; fswatch -o . | while read f; do joking_rsync; done'

It didn't work at first though because rsynch would ask for my password each time! (also I had to `brew install fswatch`)

The steps on this page worked really well to share an ssh key, and let rsynch work without bugging me. I guess I feel better about setting that up on my new mac that always requires at least a fingerprint to log in.

Earlier I had done something sort of similar and had better luck with 'filewatcher' but it has been a while for that - in this case rsync is doing more of the heavy lifting, so fswatch does the trick.

Saturday, May 9, 2020

somerville porch -- er- couchfest post-mortem

The Somerville Arts Council brought Porchfest to the Boston area in 2011, just 4 years after the first one in Ithica.  This year because of COVID-19 they switched to an online streaming-and-prepared-video model, and I helped design a robust front end. Here are some lessons learned for other porchfests and for my future self...

I started helping Somerville in 2016 by making static versions of their band list and map that would withstand the crush of traffic on the day of (very spiky traffic - a LOT of people hit your site in a short number of hours and if you have to go back to the database for each page display / band search and you're not carefully tuned, you might have problems.)

This year we asked performers to consider either: A. a live stream B. a new video made just for the event or C. selecting an existing video for the event, and then there was a fourth category of bands who had signed up before we knew it was not going to be a real-world happening, but didn't update with a new link.

I had a lot of wacky ideas about how to present this on the day of, thinking some sort of live social media feed would add excitement, but we decided to keep it relative simple. We asked for a single performance link from each band, and a description of what type of thing (live stream, new video, existing video) it was.

Here's the design I ended up with, with the top art and tile layout borrowing heavily from what was already on the live version of the site:
Traditionally the "time zone" thing has been very important to Somerville Porchfest (since when people are walking in real space, it's nice to make sure they have interesting options within walking distance.) For the virtual version, we still encouraged the use of "where you are determines what time you play live music" idea a bit (so as to stop everyone playing at the same time) but for the map, I decided to use color not for the "time" aspect but for indicating if it was a live performance (which I think is much more exciting and like a regular porchfest event) or a video (still a critical option for bands with multiple players!) And I listed the live performances first, and the premier videos after that (each list sorted by time), followed by bands who provided picked videos, and then all the other groups. Also, I put the genre information SAC had collected from each group instead of the address they were playing at. (The map at top is still interactive, you can click on a circle and see what band is located there.)

FWIW, I've started using Leaflet.js, a terrifically easy to use API - Google Maps has started a charging model that's terrible for event-based websites, when almost all the traffic happens in a short time frame... last year I started getting charged a couple hundred bucks.

Another lesson we're slow to remember year after year is that bands will always want to change things at the very last minute. When you're making a site that is a snapshot (for performance reasons) rather than reading live from the database, it's great if you can work out a way to publish changes quickly right up to show time.

Actually, we were surprised at how many bands came in the last week. For the weeks prior, it was looking like a dozen live streams, a hand full of premiers, maybe 20 odd other videos... we ended up with 46 live streams, around 20 premieres, and 20 other videos.

Some other techie and design notes:
1. I disabled "scrollwheel zoom" in leaflet, it was grabbing the scroll on laptops in an unfriendly way.
2. Originally I used softer pastel colors, and colored the whole background of the cards, but using a stronger color as trim seems to have worked out much better. (I had to swallow my ego when the people I was working with disliked the pastels :-D )
3. I couldn't find a way to change the "z index" on the map, so I had to add the circles on the map in the reverse order as they were displayed.
4. I kept this as vanilla ECMAscript. Honestly the new syntax features make it pretty easy to manipulate the data for a site and with the new quote syntax you don't even need a separate template engine.
5. A simple responsive design using flexbox worked well, setting a 500-600px viewport to start.

Next year we definitely want to switch to a model that spits out the JSON like every ten minutes or so, rather than being constantly live (which might tax the server) or static (which means we have to manually push to see changes bands make.)

Also, I think had we had most of the bands signed up when I designed this, I might have put in a text-based name/keyword filter, or more sophisticated break down by time of day. Lesson learned! But here's hoping next year will be back in full, house-to-house swing!

Friday, May 8, 2020

js one-liners - some cool JS one-liners. Maybe they would have helped with problem the other day!

Thursday, May 7, 2020

dates in JS can be ugly, capital UGH

Just shanked a techie code test in part because

It was for a "can you work with maps and arrays and stuff" exercise - which I can, but I either fumbled syntax or got mildly addled at parts and combined with some failure with the above (I had to do some basic date math) made it a hella confusing nightmare I couldn't quite come back from.

I think for some reason the second Date is like, setting in GMT and then giving me the Eastern Daylight Time equivalent of midnight in England.

Now I see why some languages insist you specify the time zone always :-D

Wednesday, May 6, 2020

the new iPad trackpad cursor

Man, I wish Apple stores were open so I could experience Apple's adaptation of the iPad cursor for better use with a trackpad... I love how the article pointed out the breakthrough of the very first mouse cursors - the idea of the cursor being the user's tiny abstract avatar. (Anyone who has played Atari 2600 Adventure with its little box shaped player will immediately understand...) And the further idea that on a regular touchscreen, that avatar is actually your fingertip. (But that means when you withdraw your finger the avatar vanishes!)

Anyway, the near bio-logical clinginess of this new cursor looks like it would feel amazing. (Though hopefully more organic and less brittle than Photoshop clones' "snap-to" which can sometimes be frustrating.)

basic GET and POST with fetch

If you don't need IE support, "fetch" is a fine, library free option for doing GETs and POSTs and the like from the browser.

There's a little bit of ceremony that goes with them. Fetch returns a promise, and then many of the parsers (e.g. ".json()" and the other Body methods) also return a promise. Here's an example wrapper for function for GET:

function doJsonGet(url, fn, err) {
        .then((data) => {
            if (!data.ok) {
                throw Error(data.status);
            return data.json();
        .then((json) => {
        .catch((e) => {

Here is the version for POST, which takes a bit more ceremony to set the Content-type and packup the payload...

function doJsonPost(url, content, fn, err) {
    const options = {
        method: 'POST',
        headers: {
            'Content-type': 'application/json',
        body: JSON.stringify(content),
    fetch(url, options)
        .then((data) => {
            if (!data.ok) {
                throw Error(data.status);
            return data.json();
        .then((json) => fn(json))
        .catch((e) => {
I made up a cleaner and battle tested version here for GET, POST, PUT and DELETE

Tuesday, May 5, 2020

forgetful jones why are you so forgetful

Heh, making a virtual Mother's Day Card, I realize I had forgotten which trig function is good for calculating the angle between two points - atan2 was the one I was thinking of.

Curious to see if I had mentioned "atan2" before, I see this old blog entry  had some pretty cool code for a character chasing the mouse pointer by gradually turning.

note to self: setting a width for mobile via viewport only is easier than you think

I admit I haven't had a lot of luck deeply understanding the Viewport Meta Tag. I had forgotten that desktop browsers tend to ignore it, so you don't have to bend over backwards to suggest a starting width for mobile without affecting desktop...

I use a simple one column layout for a lot of my P5 toys (e.g. coraferris) and so this line is the way of starting at full width:

<meta name="viewport" content="width=560">

(Someday I should really wrap my head around all of those viewport settings, and the way it interacts with CSS @media blocks.)

Saturday, May 2, 2020

xml and giant security holes

Great story of a long term iOS security hole Apple is just now fixing. The TLDR is, iOS gives apps permissions to do things in XML "plist" files, there are different XML parsers on the device to read those permission, and while normal XML comments <!-- look like this --> the weird-o semi-comments <!---> and <!--> get handled differently among the parsers, which provides a handy wedge into all sorts of shenanigans.

Over my course as a programmer, there's always this weird vindicatory schadenfreude when a technology that just smells off to be is eventually widely recognized to be kinda terrible. The original AngularJS was one huge example for me - its early terribleness made the probably pretty ok followup versions forever tainted.

And XML was like that - until JSON came around, I would have rather used tab delimited files for everything. It's just full of all these weird pointy bits, there were way too many ways of specifying what your XML schema (the rules of what fields your documents could and couldn't have) was... it was just fundamentally a tool for over-engineering for control-freak system designers.

(Original link via the ever-great Daring Fireball.)

covering up broken images in CSS

For COVID reasons, Somerville PorchFest is Somerville CouchFest this year. Organizers are encouraging participating musicians to signup to do live streams and premiere videos and the like.

You can view my current draft of the day-of design.

Some bands are missing images, and so I hunted down a way of replacing the usual gangly "broken image" bit with something more deliberate looking...

the trick seems to be that broken images will be subject to the ::before pseudo-selector but loaded images won't be, so we can jam in some content... something like

.band img {
    position: relative;
    display: inline-block;
    vertical-align: text-bottom;

.band img::before {
    content: 'missing:' attr(alt);
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    line-height: 20px;
    background-color: #ccc;
    text-align: center;
    display: block;
    width: 280px;
    height: 280px;
    overflow: hidden;

That's assuming you have meaningful alt tags, otherwise you can just leave it blank.

This page has a deeper look.

I decided to stick with Vanilla.js... or at least the ES6 flavor of it.

UPDATE: I don't have time to mess with this now, but it looks like maybe this trick doesn't work in Firefox. Better just to use a tiny bit of javascript, in my case:
<img src='${photo}' onError="replaceImageWithPlaceholder(this)">
function replaceImageWithPlaceholder(what) {
    what.src = 'porchfestweb12.png';

I feel guilty for not turning it into a practice exercise in React or Vue, but after one person interviewing me mentioned he didn't like JSX, that templates were simpler... well, I don't agree, I think JSX are pretty elegant and simple once you get the build system setup, but man... in the meanwhile the backtick quotes and templating really pull the load that used to be carried by JQuery Handlebars. This page is pretty static, but the idea of replacing all the content of a container with something made from data is pretty dang easy to implement with that quoting and array built-ins such as map and filter...

Thursday, April 30, 2020

quick thoughts on Vue.js

A few of the jobs I'm applying to are either Vue.js-centric, or a mix with some Vue and some React.

I ran through Vue Mastery Intro to Vue.js course. It's interesting how this series of courses seem so officially endorsed - and they are pretty great! Like Wes Bos React course, they have a nice  player to go through a series of videos starting with a basic core and adding complexity- but with this course, each lesson has a codepen set up and ready to go, with the lesson's code already entered and a challenge still left to do.

It seems like that "just run it!" setup is easier to do in Vue - it's impressive that you can just include one script tag and all the basics work. (Vue also has a pretty extensive build system, while React seems a bit more roll your own webpack or what not - or use the giant beast create-react-app.)

In some ways, React felt like a maturation of the best parts of jQuery, and similarly Vue feels like a bit like AngularJS. Specifically, React + JSX let you plunk bits of DOM in your JS without guilt, and so lets you use regular Javascript control structures. Vue and AngularJS take the approach of integrating conditionals and iteration into the tags. Instinctively I prefer how JSX feels more like "plain old HTML", but I could get use to Vue's approach. I still find stuff like

  <li v-for="item in items" :key="">
    {{ item.message }}

a little odd - the way the "for" is sort of a child attribute of what is properly "inside" the loop. (But it seems like Vue avoids forcing the developer to have all that weird mandatory infrastructure javascript to make the tag logic magic happen that AngularJS demanded)

I also love how Vue wraps a component - it's a bit opinionated, but you build a map-like object with data:, method:, template: etc keys -- they may well have found a sweet spot of opinionated but not TOO opinionated. I think the approach is quite favorable to React's file-component conflation.

Similarly, "emit" for having a child communicate back to the parent seems pretty keen. It's a basic idea that scales up well, to the eventBus model, or more advanced things using Vuex - a smoother ramp up then React's "either deal with children/parent state via props or use Redux"

Still, I'm not quite sure if Vue is quite as... I dunno, industrial strength as React - that might be because I've only seen tutorials for it, not real code. And admittedly the list of top sites built with React seems a bit more impressive than the top sites made with Vue.js. But both seem to get plenty of love in the 2019 "State of JS"...

Wednesday, April 29, 2020

input type="tel" regex pattern for US 10-digit phone numbers

HTML5 has a <input type="tel"> but it's not very restrictive, in particular if you have an international audience, you need to think carefully.

For Porchfests, I think it's ok to assume "US", and my partners wanted some additional data validation. Building off the example on the MDN <input type="tel"> page:

      <input type="tel" id="phone" name="phone"
      <small>Format: 123-456-7890</small>

Optional parens around area code, and then space, dash, dot, or nothing between the 3/3/4 digit sections. That will allow variants like "(555)123-1234", "555-123-1234", "555.123.1234" or straight up "5551231234".

Tuesday, April 28, 2020

es6 goodness

Prepping for job interviews, you become aware of the overlap between things you use on a regular basis (even daily) and things you might still get caught up on feeling the pressure of an interview... if you're a cautious, highly-iterative programmer like I am, there's a lot of knowledge you don't quite have in your head, but you know the basic idea and even the gotchas and you carefully test and move forward.

Anyway, I'm reposting Amber Wilkie's Everything I learned from ES6 for Everyone, which along with ECMAScript 6 - New Features: Overview & Comparison are great reminders of how not to look like an old JS fuddy-dud - lots of examples showing the old and new way of doing things.

I do think I need to up my game in staying up with hackernews and what not - ECMAScript is still evolving though mercifully the "big bang" of ES6 seems singular.

bouncing blocks in 8-bit

An online friend of mine, Matt McIrvin was talking about a cassette with some simple public domain-ish games he got as a throw-in when he got his first Atari 8-bit computer. It made me think of one of my favorite software toys, "Jane's Program":

(I keep forgetting the name of it, looking for it to be "Jesse's Program" "Jane's Game" or something like that.)

It was made by Douglas Crockford, who worked on a lot of Atari stuff including Lucasfilm's games, and later he became famous in the Javascript world - partially by helping to popularize the use of JSON, showing how simple collections and maps were much easier for humans to understand than all the pointy and obnoxious bits of XML.

(It's interesting looking at his career trajectory vs, say, Jim Butterfield, whose tech career seemed to rise and fall with the 8-bit Commodores he established his name with.)

Ha, though I just realized Crockford was not also the "De Re Atari" guy- that was Chris Crawford... an easy enough mistake to make.

Anyway, "Jane's Program" is lovely, and honestly would be a tricky thing to replicate even with today's abundance of processing power - it's not immediately obvious what kind of data structure would best capture the blocks as whole objects (with elasticity and velocity) as well as a chunky particles "blocking" columns and supporting or colliding with particles from other blocks.

Saturday, April 25, 2020

the bad ux of a bottle of bleach

So Bleach expires, yet Clorox likes to pretend that "E620050 MD2 5 18" is a good enough code to let us figure out how old it is. I think that means "made at factory E6 in the year '20 on the 050th day (i.e. mid-February) but dang, why are they being so obscure?

Making it worse it sounds like sometimes the year is one digit? and sometimes they glom on two digits at the end, before the day of year. This is a bad time for not providing an online calculator or using a more human friendly system.

Thursday, April 23, 2020

looking for my next gig

So, I got caught in a round of layoffs at my last job.

I'm moderately proud of my online In 2016 I came up with the interactive bit, that you could hover or click on technologies or jobs and the corresponding job (or technologies) would highlight.

I was also smart with my media queries so I can just save to PDF and get a lovely print version

For 2020 I cleaned up the code (still jQuery, which has a funky smell for many folks but is probably a decent tool for a document-based microproject, and it didn't seem worth redoing all the animations in CSS... jQuery really took care of a lot of little things.) and added some graphics in the same style as my header:

So, if you are reading this and looking for a top-notch UI developer, let me know!

Thursday, April 16, 2020


Interesting - yoinkbot is a Slack bot for managing shared resources. I don't have a strong opinion yet about if I dig it... if we're going to use it for QA machines, I feel like it should have more options for timing out a lease rather than just letting it sit, but still.

Wednesday, April 15, 2020

the atari portfolio

I ran into this video on the first "Palmtop PC", the Atari Portfolio - sort of a precursor to the "netbook" that came out, or maybe a bit smaller than that...

It even ran a version of MS-DOS... I was an early adopter of "taking notes in class on a laptop" (pretty rare in the early 90s) and had this beauty, the Tandy 1100FD

it didn't even have a hard drive - but the text editor was hardwired in and worked pretty well. But a Palmtop might have been even cooler.

By the end of college, I was getting into PDAs as well... someone sold me this skinny beauty, the Texas Instruments PS-9500 TimeRunner:

I got one with a neat "Clip-in Frame" that secured it in a 3-ring Binder.

That PDA was what I first started collecting thoughts and notes in - a "Commonplace Book" that eventually moved to the PalmPilot, and then morphed into my going-on-2-decades daily blog.

Getting back to the Atari Portfolio - I would love a PDA with an "Atari" button (kind of a precursor to the Windows button... isn't it weird how a software company got every hardware maker to put in a special key?) The Atari button makes me of the alternate reality in the Atari Force comic books, set in that far-off, post-apocalyptic time of 2005, where the Atari Technology and Research Institute is one of the few surviving institutions after the "Five-Day War":

UX gripe blog - Apple's iPhone PIN entry screen

So in the Age of COVID, it's almost a bummer that Apple leaned so hard into facial recognition, when everyone feels a bit safer behind a mask. (Of course, I suppose they could be wearing gloves too, but that's not quite as common.) PIN unlock becomes more necessary, and the flaws in Apple's implementation become more obvious:

First, is the lack of haptic feedback. People want to type in PINs in a hurry - both so any onlookers don't get an eyeful, and just because it's annoying! But then, it's easy to miss a beat and only enter 5 numbers when 6 were called for... I think it would be an obvious place for a confirming haptic bump as each number gets registered, but Apple doesn't agree.

And in that "I only hit 5 numbers" scenario-  most people will want to hit delete and start from scratch because they aren't sure which one was missed... so if they just start jamming on the Delete link in the lower right corner, if they hit that area one too many times, the "Delete" becomes "Cancel", and the PIN entry slides away.

So that's some Bad UX! The user is clearly annoyed, trying to just open their dang phone, and Apple's (presumably inadvertent) interpretation of "I entered most of the numbers, then hit Delete a lot" is that the user changed their mind, and want to give up unlocking their device.

UPDATE: I've been thinking about if I judged this too harshly. The trick to happier usage of it is to press one more button, get it "wrong", and then all the numerals will clear out for another try.

Also, Kudos to Apple for doing an update that recognizes a mask faster, and offers to switch to PIN that much sooner.

Monday, April 6, 2020

spreader is better

At work I joined a book club for Simplifying JavaScript: Writing Modern JavaScript with ES5, ES6, and Beyond - mostly it was fullstack engineers but the review was good for me... Like in last month's case, we had a BIG list like

const filters = [

and I wanted to conditionally add a filter in the middle (one that we were testing out)...

const filters = [

The spread operator is such an interesting answer to an asymmetry that has long bothered me in most languages with a C-like syntax - that, classically, a function can take in many named arguments, but only returns one thing. Obviously you can work around that by returning some kind of compound object, but the spread operator is really a conceptually interesting way of opening up a kind of a parallel pipeline for inputs and outputs.

(I would also give Javascript credit for really making JSON work... I mean it's a little stupid that all  valid JS isn't valid JSON but still, the elegance of the syntax, the fact that SO much in programming life is just sets (ordered or no) and maps, and that if you make those two things really concise, you can make an endrun around more nerdy, controlling syntaxes based in XML, with all those pointy tags.)

Saturday, April 4, 2020

the dumbness of smartquotes

A while back I wrote on upgrading my blog's search function so that it could search phrases in quotes - having studiously recorded quotes and thoughts for two decades, the search is a tremendous resource for me, it's a real delight in being able to reliably retrieve and correctly cite half-remembered passages and repost old links (when they're still viable, sigh.)

I tried to use my blog's search via Safari on my iPad this morning to search for the author "Karen Armstrong"... but got no results, which seemed unlikely. I then realized that searching for both terms separately came up with results that should have returned in the first search... and that the first search was working fine if I ran it from Google Chrome my laptop.

Turns out iPadOS was "helpfully" converting my typed quotes into smartquotes, and that was choking the search. This function from the Inteist website cleared it right up:

function remove_curly_quotes($text) {
    // First, replace UTF-8 characters.
    $text = str_replace(array("\xe2\x80\x98", "\xe2\x80\x99", "\xe2\x80\x9c", "\xe2\x80\x9d", "\xe2\x80\x93", "\xe2\x80\x94", "\xe2\x80\xa6"), array("'", "'", '"', '"', '-', '--',
        '...'), $text);
    // Next, replace their Windows-1252 equivalents.
    $text = str_replace(array(chr(145), chr(146), chr(147), chr(148), chr(150), chr(151), chr(133)), array("'", "'", '"', '"', '-', '--', '...'), $text);

    return $text;

I have guilt about my disdain for smartquotes - much of my professional life is balancing my engineer side - the part of me that's a little insensitive to nuance (rendering me if not faceblind at least a bit face-myopic) but very sharp on recognizing the crux of things and how systems interact - with my wanna-be designer side, that has some knack for wanting to make things look good.

If I was more of a designer, I would be horrified by how many sites and applications are content to use "inch marks" instead of proper, curled-in quotes. But the engineer in me really doesn't like the "Guess What I Mean" interface of a program doing the translation from they key I typed into what the computer believes is "proper"... and inch marks seem to get the job done pretty well in terms of delineating quotes.

(I actually admire some quotation marks used in other countries, especially « » which I just found out are called Guillemets.)

Of course, fighting against autocorrect is probably not a worthy battle. Autocorrect was a secret sauce in the original iPhone, letting our clumsy fat fingers pluck out words from these tiny virtual keys. And while I used to strongly prefer the typographical puns of :-) over the less clever 😀 - especially since different emoji sets can convey different nuanced moods - now I admit that the standard emoji banks have a lot more expressiveness and do a lot to emotionally warm chilled text to in-person conversation standards.