Sunday, June 28, 2020

the joy of craft

My friend linked to Craft is Culture which had a big emphasis on Working From Home as standard practice. (Oddly I think that's what is using the term "Digital by Default" for, which is a rather misleading way of putting it, IMHO.)

Danco writes
There are many reasons why the west coast won, but one of the most widely agreed-upon was the fact that California state law forbids non-compete clauses.
I only agree with the part of that before the comma. For my career since the mid 90s at least, they've always been thought of as probably not-enforceable and generally ignored, though obviously the same explicit-IP-protections apply.

I'm not sure of all the reasons for the coast migration. MIT + Harvard and then military contracts like Lincoln Labs and Raytheon were early anchors. But other companies like Intel, Microsoft, Atari, Apple, those were all West Coast, and the new anchors.

I think that there's a limited time when a techie is likely to make a big move to follow a job (though I guess I can think of more examples than I first realized if I try). But there's a big anchoring effect... you go to college, then you either stay in that area, return to your hometown, or maybe land a new job someplace new to you. Then there's about one period in your late 20s where you might relocate again.

The thing about follow your craft is... I think the majority of techies are rather bad at monetizing their craft. They really rely on businesses to make money. Many coders are as stupid about the very basics of how a business can scale up to afford salaries (and health care) and rent - as stupid about those basics as the non-techies are about a full website works. So the 40 hour grinds continue.

It follow from that that I think most techies are fairly risk averse. They might takes some swings w/ a low salary but uncertain high reward in a startup, but that's about it. Entrepreneurial crafters are not so common.

Danco brings up the Cathedral and the Bazaar - and indeed, Linux is amazingly impressive. But I think projects like that work by programmers "scratching itches" as they say. I think Linux was an usually fortuitous mix of some folks who wanted to take on the "big itch" of a whole damn OS, and a legion of people happy to work a bit smaller. Going back to my earlier point, folks coding for the love of craft are usually not working on something that will be as universally useful as Linux has been - or anything that they have a real hope of making a living off of.

easy set of radio buttons in react

It's funny, a few basic elements are a bit tricky in pure-function React - but then again checkboxes  and radio buttons and select/options could be a bit of a semantic pain in JQuery too.

(One real pain was using setInterval - I can use the approach outlined here but it's kind of brittle, like it needs to be inside the render...)

Anyway, here is some quick and dirty code for making a set of radio buttons:
const RadioSet = ({ selectedVal, setter, valToCaptions }) => {
    return Object.keys(valToCaptions).map((val) => {
        const caption = valToCaptions[val];
        return (
            <label key={val}>
                <input onChange={() => setter(val)} type="radio" checked={val == selectedVal} />
                {caption}
            </label>
        );
    });
};
In my case selectedVal and setter are what I get from useState(), and valToCaptions is a just a simple object of key/val pairs. Nothing fancy but I stopped violating DRY with so many radio buttons...

Friday, June 26, 2020

simple checkbox in react using hooks / useState

One of those, oh right duh --
const [thereminMode, setThereminMode] = React.useState(true);
and then the checkbox:
<input checked={thereminMode} type="checkbox" onChange={() => setThereminMode(!thereminMode)} />
It took me a second to realize, we don't have to read anything from the event or the checkbox itself, the verb "change" is enough to do the toggle ourself...

preventing nativeEvent offsetX / offsetY from being blocked by child

"Last night Wes Bos saved my life"...

Well, it was this morning, not last night. And maybe "my life" is overstating it...

But Wes Bos (his React for Beginners class is stellar, and I need to get back to his Javascript 30 - seems like serious playful fun) had a tweet that was hugely useful.

For yesterday's bad react paint program turning into a music tracker, I added a large row hilight div, but it became obvious as the hilight kept jumping that it was blocking the handleMouseMove logic that was getting
const { offsetX: x, offsetY: y, buttons } = e.nativeEvent;

(The offsets were wrong, I guess because the "target" was changed)

That tweet thread suggested some weird updated math when the event target wasn't the current thing (but how to get the "current thing" in React is a little weird?) but luckily there's a trivial (and oddly CSS-based!) solution: on the child element, putting

pointer-events: none;

lets the parent div do all the math.

(That explains why some of the pixels in my bad paint program show up in the top left corner...)

Thursday, June 25, 2020

loading extra files (libraries and binary/sound files) in a parcel app

This is Parcel 101 (or maybe 102) but as I make version 2 of a vanilla js app in React using Parcel, sometimes I forget how things work in this kind of bundled app... you can't just refer to external js libraries in a script tag, and you can't just load a sound file via URL.

The library in question is pizzicato.js, and I put a bunch of files in "src/wav/sound_##_##.wav" where the ##s are two digit numbers.

The lines of code then are:

import Pizzicato from './js/Pizzicato.min.js';
import bundledFiles from './wav/*.wav';

Using Pizzicato is then straight forward (new Pizzicato.Sound() or whatever).

The sound files are a little weirder, you get a js object where the keys are what was put in the wildcard, so sound_06_27 was the key used for the ./sound_06_27.c7ca9f08.wav which was a path I could pass to Pizzicato.

Mischnic's answer on this page was helpful in figuring out the syntax.

Oh, and luckily I remembered Parcel's weirdness in assuming it is serving from the root URL of a site, I overcame that with this line in package.json:
 "scripts": {
        "build-prod": "parcel build src/index.html --public-url ."
The "--public-url" makes it happy to go where it is put.

building a bad paint program in react with parcel

I've had some success making the Atari Sound Forger, a webapp that lets you play with sound files representing noises an Atari 2600 can make by treating the computer keyboard like a specialized piano keyboard. It can even record the loops as you bang them out and play them back in browser or generate a small batari Basic program to listen on a real Atari or emulator!

One user ("Random Terrain", gatekeeper of the batari Basic command list suggested it would be more fun/useful if you could paint on the "piano roll" display ala Mario Paint or what not) - and damn it he's right.

The Sound Forger uses some semi-clever techniques with setInterval and setTimeout to let the browser do a fair job of pretending to be an Atari, but I kind of painted myself in a corner... fundamentally it's recording sound start and stop times in milliseconds (i.e. rather native to Javascript) and then extrapolating to the "frames" (60 per second) the Atari uses. (The sound engine is a pair of single voice/single timers that switches between notes and periods of silence) The algorithm screws up when you overlap sounds though... also while it worked pretty well for "sound on, sound off" approach, more complicated ADSR/sound envelope technique (i.e. hitting the note then reducing volume, like a piano striking a note) was going to be clunky at best.

So I decided to start from scratch, but grabbing tricks and techniques from my old code. Also I thought it would be good practice to switch into React, rather than doing yet another Vanilla JS project.

(Actually, it might make more sense to switch to P5.js, like I did for my playfield graphic editor playfieldpal-bB-DPC  - or maybe at least use a canvas-in-react approach which I would have to study up on, vs the "lets just throw DOM elements at it" approach I'm starting with)

I'm also a little burnt out on the "ALL THE THINGS!" approach of create-react-app, so I decided to fire up parcel. During that process, I (think I) had to update node and even download xcode on my new machine because of some js dependencies.) Also, the one bell-and-or-whistle I wanted was using CSS modules, so I had to follow a few more steps, running
npm add postcss-modules autoprefixer
and then making a .postcssrc file with
{
  "modules": true,
  "plugins": {
    "autoprefixer": {
      "grid": true
    }
  }
}
after that importing a "Widget.module.css" worked as expected and I had nice bundling of components with the CSS that applies to them only.

I decided to start with a simple Proof of Concept paint program... cutting to the chase I ended up the stuff I put in this codesandbox.io. (Out of laziness I punted and went back to normal CSS inclusion for the sandbox)

I used the useState hook, and to keep it simple I collapsed what might have been a 2D array into a map-like object by having the key be x + (y * width) (old programmer trick).

I'm plucking things out of the nativeEvent property of the event object my handleMouseMove function gets:
const { offsetX: x, offsetY: y, buttons } = e.nativeEvent;
I'm not quite sure what e.nativeEvent is... I could dump it out to console.log() ok, and pull out the stuff I need (offset values so the mouse position relative to the top left corner)  but if you go over the keys or call JSON.stringify on it it claims the only key is "isTrusted" (set to true).

So, it's a pretty lousy paint program, missing a lot of dots! (when I made a simpler editor, I used a trick of treating the previous mouse position and current as line endpoints to get all the dots inbetween). But for the app I have in mind, the grid is much less "fine" (it may even use clicks instead of drawing for the most part) and I think this technique (of putting small pixel-y <div>s  over the parent space for the dots) should work ok.

Wednesday, June 24, 2020

remembering AOL instant messenger

Nice tribute to AOL Instant Messenger. I feel like my friends and I were funnier on AIM than we are on the current chat options. Maybe it's the setting? Like it's easier to think of something clever typing on a normal computer with a keyboard and a big screen than tip-tapping on a mobile device?

The article mentions the art of the away message... I think that tends to be a youth thing. Like in college, we had ".plan" files, what people would see when they ran the "finger" command on your account (and yes, the jokes about that verb were plentiful and rarely subtle). It shared that youthful romance energy as mentioned in the article, wistfully seeing if your crush had logged in and checked your email and not bothered to reply, and leaving a message that you hoped they might see but might never know if they did.

(Not even sure if there's a social media equivalent of "away messages" and .plans - maybe avatar photos and what the banner image on your FB profile page?)

gmail feeling like an old car

A piece going over some of the features of the Hey e-mail service, and how they cleverly borrowed interface patterns from other things like social media and the MacOS dock to make a better e-mail experience.

It makes me realize that I've been using gmail for about as long as I've had my current car, about 16 years. Besides the value of 16 years of mail archive (and my hesitation to use an email service where I assume I'd lose my account if I stopped paying - "free" is a dangerous and addictive drug!) the gmail feature I'd miss if it wasn't well-replicated is sorting my inbox into "Important and Unread" vs "Everything Else" (with a little section of starred items to get back to.) That separation of the sheep from the goats works better for me than more fine-grained categories.

Monday, June 22, 2020

good dev articles

Underrated HTML tags - big fan of details/summary, hadn't heard about progress...

I have a hard time being snarky about clichéd font choices, but this article has some good counter-suggestions as well.

10 Tips and Tricks That Will Make You a Better ReactJS Dev - I think the title is misleading, but it's a great overview of some of the more recent trends in React land - Functional Components w/ Hooks, Context API, Styled-Components, Fragments, Error Boundaries, Typescript, Jest/Enzyme Testing, Conditionals, Higher-Order-Components, React Dev-Tools, Plug-ins for VS Code.

Here's a trick for better names for Styled Components - just the other day we noticed that was kind of a problem, nice to know there's a fix for it, setting .displayName on the styled.whatever`thing`.

Friday, June 19, 2020

creative coding (with face tracking and sound generators!)

Man, I love P5.js and creative coding -

The use of clmtracker to do some basic webcam face recognition work was especially inspiring.

UPDATE: I decided to retrace her steps and it was a little tougher than I expected - it's not easy to follow code footsteps in general and in particular proper loading of the clmtrackr library was a oddly challenging - the CDN link she uses seems to be defunct, and a few other attempts to pull from a CDN, from github, or my own webserver didn't work (blocked by CORS errors) but finally I got it by downloading the raw clmtrackr.js file linked to from the github README, then going to the left sidebar (the arrow under the "Play" button) then hitting "Sketch files | Upload file") and putting it there, then editing index.html so it pointed to it:
    <script src="clmtrackr.js"></script>

At first I didn't do the sound oscillator she does, but here's a version that just puts circles on all the tracking dots and here's one where I play with making a virtual mask with a responsive mouth, like a very poor version of Animoji...

(The face tracking really isn't great, sensitive to both lighting conditions and me wearing glasses.)

Finally I put the same kind of sound oscillator she does back in for a full mask and sound fest... hold the mouse to make a noise, the distance of your nose from the center controls the frequency, and how open your mouth is is the volume...

Thursday, June 18, 2020

"I could hit a dozen with a bread roll from where I'm sitting."

"Now wait," he interrupted before Richard even had a chance to start, "don't I vaguely remember that you had some sort of computer when you were here? When was it? 1977?"

"Well, what we called a computer in 1977 was really a kind of electric abacus, but..."

"Oh, now, don't underestimate the abacus," said Reg. "In skilled hands it's a very sophisticated calculating device. Furthermore it requires no power, can be made with any materials you have to hand, and never goes bing in the middle of an important piece of work."

"So an electric one would be particularly pointless," said Richard.

"True enough," conceded Reg.

"There really wasn't a lot this machine could do that you couldn't do yourself in half the time with a lot less trouble," said Richard, "but it was, on the other hand, very good at being a slow and dim-witted pupil."

Reg looked at him quizzically.

"I had no idea they were supposed to be in short supply," he said. "I could hit a dozen with a bread roll from where I'm sitting."

"I'm sure. But look at it this way. What really is the point of trying to teach anything to anybody?"

This question seemed to provoke a murmur of sympathetic approval from up and down the table.

Richard continued, "What I mean is that if you really want to understand something, the best way is to try and explain it to someone else. That forces you to sort it out in your own mind. And the more slow and dim-witted your pupil, the more you have to break things down into more and more simple ideas. And that's really the essence of programming. By the time you've sorted out a complicated idea into little steps that even a stupid machine can deal with, you've certainly learned something about it yourself. The teacher usually learns more than the pupil. Isn't that true?"

"It would be hard to learn much less than my pupils," came a low growl from somewhere on the table, "without undergoing a pre-frontal lobotomy."

"So I used to spend days struggling to write essays on this 16K machine that would have taken a couple of hours on a typewriter, but what was fascinating to me was the process of trying to explain to the machine what it was I wanted it to do. I virtually wrote my own word processor in BASIC. A simple search and replace routine would take about three hours."

"I forget, did you ever get any essays done at all?"

"Well, not as such. No actual essays, but the reasons why not were absolutely fascinating [...]"


--Douglas Adams, "Dirk Gently's Holistic Detective Agency"
This anecdote -in particular the "I could hit a dozen with a bread roll from where I’m sitting" line - has always stuck with me, and I thought of it when I was talking about programming to a friend (she brought up a lesson she had had at computer camp, where you have to give painstakingly step-by-step directions for making a peanut butter sandwich to another camper who has to follow them extremely literally.)

I do think that anyone who can write down a recipe a beginner can follow is in good shape to try programming.

Tuesday, June 16, 2020

tic-tac-toe

Heh. Just noticed the React tutorial is the same programming task I gave myself as a 9 or 10 year old kid: a 2-player only Tic Tac Toe game. (Ok, no, this is not demonstrably better than a piece of paper, that's not the point!) What's really striking for me is that it uses the same method of determining if there's a winner that I came up with as a kid - just see if any of the 8 ways of winning are filled with all "X"s or all "O"s. (I remember my Aunt saying she was impressed that I came up with that when I geekily showed off the method to her...)

some decent js and react articles

Learning JS synchronicity via a series of horse races is a clever way of showing how JS itself is pretty synchronous, but has asynchronous libraries and environments.

Write clean(er) Components & JSX was some pretty clear, no-nonsense advice showing common semi-mistakes, why they happen, and what's a better solution.

React Hook Form VS Formik

Stop Mocking Fetch - I like the way this person thinks about mocking stuff up. I've had some peers who I thought were otherwise brilliant, but when it came to tests, they encouraged "just make sure the mock is called the right number of times" and not worry if the right info was being passed into it, or even if the client was doing the right thing with the data...

Thursday, June 11, 2020

css hack: win the specificity wars by doubling or tripling up at the classname

Heh, styled-components.com advanced page had a hack that I hadn't thought of but seems obvious in hindsite:

.red-bg.red-bg {
  background-color: red;
}

Like, make sure it REALLY is read even if there was a .green-bg class defined as well.

Well, beats !important, I guess?

Wednesday, June 10, 2020

git 101

So, a bit of git 101, but not something you actually do that often: creating a new git repository, both locally and on your primary remote, from an existing folder (like maybe something created with create-react-app or similar) Like, I wasn't sure if you had to start making the remote archive, or you could push it straight to remote from local.

The answer is you kind of meet in the middle - set up the local repository, go to github and do a new repo there, then tell them about each other. This page walks you through it, but for future reference, the important thing was:
in the folder, do a

git init

then maybe a

git add .
git commit -m "first commit"

and that sets up your local repository

Then go to github, hit "New", give it the (probably same) repository name...
github then gives you the command hint you need, "...or push an existing repository from the command line":

git remote add origin git@github.com:COMPANY-ETC/WHATEVER-THE-NAME-WAS.git
git push -u origin master

and Bob's Yer Uncle.


good piece on design systems

Everything you need to know about Design Systems ... probably doesn't live up to its name but still is good.

atari 2600 homebrew programming - 88 lines about 32 pixels

I've been getting back into programming the Atari 2600 again.

This past week I updated my 2015 Global Game Jam project Loaded4Bear - now it has an aggresive but not too bright AI opponent option.


I've always liked Atari games that have the computer playing by the same rules as the human, and long was thinking I should get back to add that to the Loaded4Bear. - I wasn't sure if I could! So I'm chuffed to have done it.

Also, the music by Oliver Getz is amazing- I showed him the basics (no pun intended) and he did some great work back then.

This is all made feasible by batari BASIC - my first homebrew in 2004 was in assembly and cost me a year. The GGJ version of this game took a weekend, thanks to this language.

The canonical place to learn about the bB is Random Terrain's batari Basic command page though it is rather overwhelming! There are more tutorials and information at the AtariAge batari Basic forum.

Many bB users like coding in "Visual batari Basic" as an IDE though, besides the fact it's Windows only, I never could get my head around its version of asset management. There is a pretty good plugin for Visual Studio Code called "Atari Dev Studio" that makes the process look more familiar to folks who program as day job. It's cool to be able to copy and paste in a program listing, click the button, and have it run in an emulator.

I hacked together two simple P5.js tools for drawing the Playfield (background) graphics - one for the old school regular playfields and then one for the  highrez 88-line DPC+ playfields... for the former, it creates an entire runnable program, and the latter outputs something you can put in a standard DPC+ template. (The ability to just copy and paste into a single file, and then run and have a game, harkens back to the old days of magazines with type-in-listings. (Also Java Processing/P5, come to think of it) - just so elegant to have pure text turn into graphics and interaction!)

The DPC+ thing-- a lot of Atari 2600 homebrew has gotten away from the classic "4K Rom, 128 bytes memory" and using "DPC+" which offloads much of the processor work onto an oncart-chip. There's a temptation to think of this as cheating, but it's based on what Activision was doing with Pitfall II, and then what companies like Nintendo would do with Star Fox etc. Still, I think there's much charm to seeing what kind of a game can be made using the original limitations of the basic hardware and sofware.

GEEKNOTE on those editor tools I built - it was great to be able to make them in a hurry! (Though a little weird to be using such power technology in the tool chain for such old stuff.) The only bit of cleverness was A. a flashing cursor box to show you which pixel would be set and B. at first I just drew whatever pixel was under the mouse during the "drag" movement, but if the mouse was moving quickly, spaces in between would be missed.

I went from this simple check: (edited for clarity)
function mouseDragged() {
      const x = int(mouseX / PIXW);
      const y = int(mouseY / PIXH);
      grid[x][y] = isDrawing;
  }  
}
to this loop that imagines a line with 100 intervening points from the previous mouse position to the current mouse position, and runs the same check for each pixel underneath -
function mouseDragged() {
  for(let i = 0; i < 100; i++){
      const tx = map(i,1,100,pmouseX,mouseX); 
      const ty = map(i,1,100,pmouseY,mouseY); 
      const x = int(tx / PIXW);
      const y = int(ty / PIXH);
      grid[x][y] = isDrawing;
  }  
}
A little redundant but works great!

Tuesday, June 9, 2020

worst chart ever?

Wow. Daring Fireball pointed to a horrible, misleading, terrible, idiotic chart from CNBC.

The events are pretty simple to understand. One month, we lost 20 million or so job. The next month, we get about 13% of those back. (Or other jobs show up.)

How should we display that? NY Times showed "number of jobs" - starting with a baseline of 130 Million half way through the first Obama presidency, then steadily increasing until this spring. Finally, a plummet, with a bit of a bounce:


Here's how CNBC chose to display the same phenomenon:


It took me a second to even figure out what it meant. It's like "rate of change" in calculus or physics... from 2015 we see a small positive value, new jobs added. Then we lose a HEAP of jobs. Next month, we gain more jobs than usual - but all that means is 10-15% of the jobs came back, or got replaced. Not so good if you're one of the 85-90% of the millions who lost theirs!

Gruber pointed to this twitter thread with a few more honest depictions.

UPDATE:
looks like it's now like this:


that's a bit better than I guess. Using a bar graph shakes the idea that there is meaning to the area under the graph...

Monday, June 8, 2020

visual studio code: settings by User, Workspace, or Folder...

It's a powerful and easy to use idea once you know the trick, but it's easy to miss at first: customizations in VS Code are "by user" by default, but you can do it "by workspace":
In this case, I'm doing more work across packages (including mockup companies "PurpleCo" and "GreenCo") and setting a color theme that reminds me which space I'm in is great.  ("Quite Light" is my usual go-to.... and I still wish "Dark Modes" weren't quite so popular, for folks with astigmatism it's a world of dancing after images.)

Sunday, June 7, 2020

silicon valley is all about the database shoving

"One could criticize no-code for not offering the flexibility and nuance you can get by writing your own code, line by line. But the truth is, for all the hoopla about Silicon Valley's innovative genius, a huge number of apps don't do much more than awfully simple things. Seriously: Silicon Valley's main trick is just shoving things into a database and pulling them out again. I'm exaggerating, but only a bit."

Thompson is so right about the "trick". There's lots of details to get right - making sure people shove in the right stuff (UI) making sure the wrong people can't then pull out other people's stuff (security) ensuring lots of people can shove (scalability) and coaxing people to put the stuff in (UX) but 80-90% of everything is this database shoving.

That's also adjacent to a model I had to make to describe the priorities of some new-fangled systems for UI - there's been a strong tend towards "declarative" programming for making web apps - with an assumption that the best thing a UI toolkit could do is make it super easy to have the webpage instantly update itself to reflect the content of the data in the browser's memory - that way the programmer doesn't have to manipulate the page, just massage the data. It took me a while to realize that one reason I was slow to see the appeal is that I never thought the "in browser memory" mattered much - the stuff in your server's database is what really matters, and the presentation of it on the webpage to the user matters, but anything in between (like the data in the browser's memory) is just an artifact.

Friday, June 5, 2020

running your own npm repository with verdaccio

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.

UPDATE A few weeks later, I'm more fond of verrdaccio. My guess is that using it is a more "real" experience than using `npm link` when you're pulling from your local repos, and it smoothly passes through requests for all the other packages that's aren't local, so
npm set registry http://localhost:4873/
is a safe enough thing to do - but then you have to make sure it starts up every time you log in or run it yourself (I kind of like having it in a background terminal window and watching it handle all those requests, especially now that I have an extra vertical monitor to run it on.)

Publish command is npm publish --registry http://localhost:4873
 

UPDATE 2: fair warning, if you're using verdaccio and npm, your package-lock.json might be full of localhost:4873 ... they say npm is now agnostic about which registry you used to generate the package-lock.json but I'm not sure if that means the hosts in the URL don't matter??

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.