Thursday, April 25, 2019

ow it hertz!

Reading this tweet thread about Hertz suing Accenture for a failed massive project is interesting.

clever "nobr" for typography finesse in responsive design

Work is shifting to a more designer-centered approach.

On a new page, we had a headline with an ampersand, along the lines of:
Getting to Know Our Dedicated & Talented Professional Staff

Our Director of UX was wondering that if we could take the line that was breaking after the ampersand like this:
Getting to Know Our Dedicated & 
Talented Professional Staff

and switch it to the more typographically standard
Getting to Know Our Dedicated
& Talented Professional Staff

Easier done than said, right? Just put a <br /> in there after "Dedicated" and call it a day? Well, no, I realized after a little bit - there's a risk that mobile might have different ideas about the break:
Getting to Know Our 
Dedicated
& Talented Professional 
Staff

Whoops!

But what's the fix? I have tags and CSS to say "don't line break this" but not so much for "break here first if you have to". My first thought was "huh... maybe a media query so that linebreak only appears when the width is wide enough to be needed?" but that seems hacky.

Turns out "don't line break this" is what we want - we want to keep "& Talented" together as a unit, so either "Dedicated & Talented" all fits on the line, or "& Talented" starts the next one.

The oldschool, not-quite-standard way is to use <nobr> - a more modern way way (but still has non-semantic markup) is a span with the style "white-space: no-wrap;".

Wednesday, April 24, 2019

order in the court

Historically my company had the inmates engineers running the joint, but as the company has matured, we added specialized product managers, UX, and now, dedicated designers.

Designers and UI engineers have to establish the boundaries for "pixel perfection" vs. what is expedient in the current code base, and with ideas such as making things as responsive as possible, even when there are varying designs for mobile vs desktop.

In one job (which of course was part of a hair-on-fire we need to get this out tonight rush) the desktop looked like this:
And the mobile might collapse that into two columns:
The astute might observer might notice some tweaking going on - especially in the order, assuming these were flexbox'd or otherwise allowed to flow/ wrap.... from a designers point of view, it's maximizing (or rather, minimizing) use of space, and also with an eye toward keeping matching icon colors apart.  From an engineers point of view, it's a bit messy to have one code base that can do either...

Anyway, the short of it is, flex-box items can have an "order" CSS property that let me line up things in the most space efficient way. Now, my good engineer soul thinks that feels a bit awkward to have to apply to all items, but to get a little responsive mojo working it's pretty handy - there was already a parent class that was different in mobile mode (this part of the site is reactive though we are designing more and more in a responsive kind of way, so we could also use media queries) so just applying the order in the mobile case was easy.

Tuesday, April 23, 2019

imagemagick fixed heights suitable for thumbnail sheet

A while back I mentioned using ImageMagick to make CSS thumbsheets.

I do this for the Somerville Porchfest website and every year it feels like a wrestling match - with all these bandphotos some are "quirky", either portrait mode when everyone else is landscape, or else just "really weirdly long landscape.

My process is a bit clunky, but I use so many command-line-ish tools I'm not sure it's worth automating fully... instead I generated a webpage that constructs shell commands to run by hand.

The first step is to take the raw JSON from the Somerville Arts Council site and parse it into a file I call "bandjson.js". My webpage shows me a list of all band IDs and the corresponding thumbnail URLs that I can inspect by hand for any weirdness.

For each of those I extract the extesnion (usually jpg or png or JPG or jpeg or JPEG)  then I construct a
`wget -O ${bandid}.${extension} "${thumbailurl}"`;
which gathers all the files locally and normalizes the filenames to bandid.ext

I make a bit of .js then that maps bandid to bandid.ext

The painful bit for this year was working out this normalization, say for 46702.jpg:
convert '46702.jpg' -resize 100x67 -background white -gravity center -extent 100x67 '46702.jpg';

the background/gravity/extent arguments are what makes sure the image is at LEAST as big as those dimensions (centered on a white background in this case). Without that, I'd have to do weird work to read and track the size of each thumbnail separately.

After that I make a single honking big Imagemagick command line argument to glue them together, using \ at EOL so I don't overrun my shell's limit for single commands
convert \
 'bandid1.ext'\
'bandid2.ext'\ 
-append thumbsheet.jpg

I then build another js file mapping bandid to it's offset # in the sheet- I can then multiply that number by 67 (the height I requested on the convert operation) to generate the CSS - each image is width 100px, height 67px, and then the background for say the second one is:
background: url(thumbsheet.jpg) 0 -67px;

I'm not positive if all the work is worth it, maybe I should just have the 153 images on the page, but that feels weird to me still.

Friday, April 19, 2019

use your words, gadgets!

For a few year I've been partial to BeatsX headphones - honestly all wireless audio solutions lack gravitas - the Borg look of oldschool Bluetooth handsfree things, the double Uhura of Airpods - even over-the-ear big headphones look like earmuffs, or maybe Lobot from Empire Strikes Back. The "librarian eyeglass holder strap" look of BeatsX weren't so bad by comparison, and I liked how they use the same Apple W1 chip as Airpods - but you can take one out for a bit without having to hold it or put it away or have your music stop.

But I'd been through too many pairs - lost one, misplaced another, and then my main ones started losing sound on one side. (The build-quality / feel of them is great, but the durability just so-so.) So I went to Best Buy and discovered cheapish (~$30) earbuds of decent quality are now a thing! I bought a pair of Sony's WI-C300. This review agrees the sound quality is pretty decent, which is all I need - I tend to use other things for music, and have these for walking around to podcasts, and despite being a musician I'm not much of an audiophile.

Overall they aren't as comfortable as the BeatsX - the wire is a bit short, and that wire's skinniness is much less appealing than the Beats ribbon, and the buds don't have magnets (in theory useful to complete and secure the "necklace" feel when not in use, in practice it's just pleasing to have magnets to play with) but in one simple way it's much more comfortable - when you power them up, a chime plays and you hear a voice say "power on". As the things connect to your phone, another chime plays and it says "bluetooth connected".

That is SO much better than the little musical blurbles BeatsX play. There's kind of one that goes up to say "on" or something, and another to say "powering down", and maybe some chirp when it connected to the phone but I was always having to yank the control bit around to look and see if the little light on it was shining or not, because it was pretty ambiguous. (This is not helped on modern iPhones, where they don't have display area to spare to always show you if you're in headphones mode or not.)

Good old Bluetooth plus a voice explaining the status is way better than anything I thought I was getting from Apple's W1 chip plus nearly meaningless musical ditties! I think there's a simple UX takeaway here - just use words! (Ideally with an option to change languages, but I'd rather have a human language I didn't know than try to decode the musical language of Beats designers.)

(FOLLOWUP: After getting the Sony's, I also grabbed a pair of neon green iLuv Sound Air 2 - it also has simple voice feedback, but the connection wire is closer to the flat ribbon of the Beats, also a bit longer than the Sony one.)

(FOLLOWUP TO THE FOLLOWUP: having found the BeatsX I misplaced, I'm going back to them. Ultimately, the slightly greater length of their chord relative to the other ones, along with having two symmetrical battery-ish packs rather than one in the back, means they're more comfortable - especially when not in use - they will stay put as a "necklace" while the iLuvs will slip and slide off. Still, I wish there was an option to have a voice say power on/off or connected...)

Sunday, April 14, 2019

really old web design

I was looking at the late-90s rendition of my website, back when they were called "homepages" (Hooray for the Internet Archive Wayback machine! That site,along with the original versions of Google, Youtube, and Shazam, are things where the magnitude of what they were doing just flabbergasted me and still impresses.)

Anyway, I had to pull up the devtools inspector, because it wasn't clear at a glance how I achieved the layout of my photos page - it has a kind of pleasing irregularity to it. I knew it was tables, of course - turns out it's a rough two-column layout, with on-again-off-again use of the img align tag - I may have just been playing around with how to sort-of-align a set with portrait, landscape, and square cropped photos.

I'm thinking of this time in part because of the 25th anniversary of Tufts sQ!, my college acapella group. And I remembered that my early web designer self was strongly influenced by my camrade Erica's sQ Official Homepage - both the "columns but not rows design" and the use of transparent-background photo icons (in particular, the airplane on that page) were strong influences on my early sites.

Sometimes when I'm looking at my old raw HTML I'm delighted to see it all in caps - a habit I believe I got from Perl and other technical documentation at the time. )It's not clear if I had outgrown the habit when I designed that page)  These days it's unacceptably uncool (probably even deprecated, since xhtml-stuff was in all lower case, I think by mandate) - on par with using tables for simple flexible layouts - but honestly, in an age before fancy highlight editors, it made it very easy to discern what was content and what was markup...

Wednesday, April 10, 2019

the underbrush

As I go through and groom old blog entries and see just how many broken links predominate, how the old geek vision that "urls can be forever" is almost completely unrealized, but sometimes I can use a search engine to find a replacement for a yanked Youtube video, I realize that one purpose of Google is weeding out the underbrush of fallen sites, if search wasn't a constantly renewed process and we relied on old archives we'd be choked in a forest of dead wood.

Tuesday, April 9, 2019

UI hall of shame: Samsung NU8000 onscreen keyboard

Last December I bought myself a Christmas gift, a lovely 65" Samsung NU8000 television. (I miss my old projector, but given my space, this is the smallest size that still feels like a billboard, and Wirecutter likes the low-lag mode of these for videogames)

Overall its UI is pretty grand - much better than the older Samsung my mom has - all the apps are easy to get to and most are of good quality, and the shortcuts from the main screen generally make sense (and I'm not TOO worried about the data collection I'm sure this beast is running on me.) But the onscreen keyboard they use for some of their setup/config is THE #&$#(*$@& WORST, especially for entering a password. 

It's kind of a standard "use the cursor up/down/left/right, select the letters via an onscreen keyboard" (laid out like a normal typing keyboard) that video game console owners have put up with for a decade BUT -- 8 letters or so into typing a minium-length-8-character password, say -- once you select a letter, the space immediately to the left of the character you just typed becomes a checkbox, a short cut to "I'm all done".... so if you're bopping along, trying to efficiently enter your password, and it starts with "ireallyreally" in it... you enter the r of second really, that's the eighth character - meaning you've meant the minimal wifi password length, so then when you go to move the cursor to the left, you're not over the "e", you're about to select the "I'm all done" checkbox.

And you hit the center of the button thinking you're on the e, and the thing thinks you're done, and takes away the screen to try out your (of course incomplete) password, and you have no idea what is going on, but you're on a totally different screen entirely, and you don't know what the hell happened, so then maybe you repeat this dance two or three times, because it's such #$#(*@ STUPID HALF-BAKED DUMB@#$#@ INCOMPETENCE in trying to make a "helpful" - yet fundamentally unpredictable -User Interface

A core of "Tao of Programming" is the "Law of Least Astonishment", i.e. a program should always do that which surprises the user least - and this is a FLAGRANT violation of that very simple principle.

Of course, even once the user has overcome the surprise of a barely-useful shortcut showing up where there wasn't one before, to REMOVE the "helpful" checkbox you simply go select another letter and the checkbox goes away- but it comes back, so for the rest of the text entry if the next letter you wish to type happens to be to the left of the one you just entered, you have to wiggle the cursor around to shake off the dang uselessly premature checkbox.

Programmers. They need to learn the difference between "can" and "should"

(Mercifully, the searches on the various apps like Netflix and Amazon Prime implement their own keyboards, free of this half-bakerd crap.)

Monday, April 8, 2019

ecmascript 6 is fire. i mean, it should be burned. (safely destructure nested js objects)

ECMAscript 6 has a lot of features. One of them is Destructuring Assignment with Deep Matching which you can combine with Shorthand Notation... so you can do something like

var { foo, bar: { baz} } = someCall();
which is the same as 
var tmp = someCall();
var foo = tmp.foo;
var baz = tmp.bar.baz;
 
One interesting note when you use that kind of destructuring is that the intermediate variables aren't defined - e.g. you don't get a "bar" in this case - "bar" is just a signpost to the structure
 
The problem is, things are very unhappy if "bar" is undefined.

I'm not sure if there's any cool syntax that might say "please destructure this, and just give me back an undefined for nodes I am implying should exists even if they having missing parent" (Because "is not defined" errors are so much harsher than mere "undefined" variables despite the superficial similarity.) 

So the boring way is to break the destructuring into steps and use object matching default values:
var {foo, bar = {}} = someCall();
var {baz} = bar;

As I ask about this at work, it's interesting how many devs I hear express some dislike for all the new synactic hotness. And I agree, things (especially curly braces) are weirdly context dependent and then there are tricks like destructuring with renaming
var { foo : bar } = someCall(); 
//same as var bar = someCall()['foo'];
that feel like a weird mix up of left operand and right operands - as if the meaning of the : has done a 180 relative to a "normal" object creation (e.g. var foo = { bar : 123 })

Saturday, April 6, 2019

eric was right - sometimes declarative rendering is better

I had a great tech manager Eric last year. He shared some of my inexperience with React and Redux and the concepts that underlie them, but he attacked the learning curve with gusto.

He told me how he made a learning/side project for a trello-like system, and for grins he implemented it with React (or maybe one of those Build Your Own Baby React systems) and then with more traditional methods. And he said, the declarative rendering system was miles ahead in speed and ease of use.

I didn't believe him.

Woe is me!

For my Porchfests side project, I needed to redo the Hourtron - as you can see with the (fairly simplistic but clean) 2015 prototype for it, the idea is you have a bunch of rows representing porches, and columns that represent performance times, and then you can drag and drop bands unto performance times / locations.

So for the first real implementation (2015-2018), I had it save all the performances on the page (aka "gigs") at once as a giant blob of JSON. I wanted to better support one band having multiple gigs, as well as be a bit more robust and be able to save each gig as it was set - I did a from-scratch rewrite (also figuring I was too pure of heart for JQuery, which was ok-ish for the drag and drop but was weirdly infeasible for the Ajax POST)

It was harder than it looked. In particular, these DOM blocks I had sometimes representing an unplaced band, sometimes showing a band at its gig - the IDs for gigs were server generated, and linking that gig id with the block in such a way that moving the gig time or place didn't generate a duplicate gig, was way tougher than I expected, and I realized the declarative method of saying "this bit of memory represents state, and the DOM can just reflect that as quickly as possible would have been great"... also the jQuery-free version of drag and drop reminded me that the "ghost" I use to show where a band will be dropped could still be handled as plain old javascript, so kind of a hybrid where the main block schedule was React-or-similar but the drag and drop was separate would have been fine.

Ah well, live and learn. I need to rethink my side project setup... I still like renting a cheap VPS and putting everything there, but I need to build up tools so that I can use tech that plays better with some kind of build system, rather than being spoiled by the "I save the raw file, reload, and run". Stuff with watchers and what not can get that feel pretty speedily, tbh, and then I have more options about the babel-ization and what not I use.

Friday, April 5, 2019

you may need jquery after all

Grrrr. Just blew like an hour on a pressing project, thinking I was too pure for jQuery--
You Might Not Need JQuery claims that
$.ajax({
  type: 'POST',
  url: '/my/url',
  data: data
});

can be replaced with

var request = new XMLHttpRequest();
request.open('POST', '/my/url', true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
request.send(data);

On firefox and chrome, this does not seem to be the case

My test script was
<?
$res = array();
$res['msg'] = $_REQUEST['bandid'];
print_r($_REQUEST);
print json_encode($res);
?>
and looking at the network panel, the jQuery version does what I expect (using the print_r to mirror what I passed in (i.e. the 'data' parameter in both cases is a simple JS object) and the XMLHttpRequest version seems to send nothing. Probably missing something "obvious" but too tired and stressed to deal with it now.

Wednesday, April 3, 2019

unpopular opinion: 'developers do their own testing' doesn't scale

Almost two decades ago, I ran into Mr. Bad's Things to Say When You're Losing a Technical Argument. My favorite was the shortest, and first on the list:
1. That won't scale
So, keep that in your back pocket.

Sometime over the past decade, the concept of "We don't need QA as a separate discipline - we can trust our developers, fortified by automated testing".

Almost exactly five years ago I wrote a blog entry about joel on software and the quixotic nature of complete testing . I still stand by most of what I wrote there - especially about how hard it is to get coders to bruise their own ego, and think outside the box.

But the "No QA" policy is so hip and trendy in tech - and yet I know of at least two companies following that path that are really struggling with frequent emergencies. And of course, it's pretty smart and productive people espousing it.

My current theory is that the idea that developers know enough about the holistic system to do a good job of knowing what will break - not just coding to prevent it, but then taking the next step of meaningful tests well "outside the box" of where they've been codin - only works with a limited scale code base. As a team grows and codebase ages:
A. you probably have a smaller proportion of "rock stars" than you had early on
B.  the newer people haven't witnessed the growth from the simple core to whatever gnarly forest you now have, knowledge of which helps in intuition and sleuthing
and
C. just the number of connections is increasing, exponentially or at least geometrically. There's just more there there! Maybe- just maybe- "developers do their own testing" doesn't scale across time and space.

This is why my favorite work setups involved a small group of dedicated QA folk. They were engaged enough to ask good questions (different from that "oh we'll let offshore QA bang on it overnight" I've seen elsewhere), smart enough about the codebase - and at a high enough level - to have a gut feeling for where a new change might be problematic. It's a bit adversarial (but in a congenial style) in a way that a coder finding their own bugs isn't, even if the coder has a reputation to protect.

Tuesday, April 2, 2019

php notes: a directory full of json

Like I said in 2015,
Good lord, I know I'm not impressing anyone by digging on PHP, but man does it do a good job making easy things easy.
For a while now, I've liked doing my serverside plumbing in PHP and keeping my UI options open in javascript.

One of my favorite robust poor-man-db's paradigms is storing flat JSON files in directory - roughly, a directory is like a table, and each file is row. (GUID-ish filenames acts as a rowid). There are some downsides (it's not easy to rearrange columns, reading in a whole table is a large-ish amount of file reads, etc) but it's super-robust, hyper-portable, easy to implement and simple to operate on the data without simple text editors.

And sometimes, I just like to squirt a whole dang "table" like this into a page - here's some PHP that I'm sure I've recreated many times that does that:
    function dirOfJsonToFilesToJsonString($dir) {
        $files = array_diff(scandir($dir), array('.', '..'));
        $guts = array();
        foreach ($files as $i => $file) {
            $raw = file_get_contents("$dir/$file");
            $guts[$file] = json_decode($raw, true);
        }
        return json_encode($guts);
    }
That comedy gem wraps the full table contents as a JSON object/map string, and then I can let javascript do all the UI work.

Again, no one is impressed, but I'm one of those who believes in taking PHP seriously ;-)