The CSS SMEAR hack
Tags: css, tutorial, programming
Published: 2022-07-12 12:57:26

The SMEAR technique enables infinite generations of simulations, games, or other CSS experiments to happen without any JS and without revealing the sauce to the User.

![gif of SMEAR being used](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eeqhh697cjefcmexfvqi.gif)

SMEAR stands for Shrouded Markup Editing And Regeneration - a description of what happens to advance the application state to the next generation without JS. The smearing gesture itself has to be more deliberate and slower than a swipe, and is where the name SMEAR actually comes from. Forcing SMEAR into an acronym happened after the fact as a terrible joke.

{% embed https://www.youtube.com/watch?v=7QrBqCifZKs %}

The SMEAR hack is somewhat involved in the details, so to navigate that complexity, let's walk backwards from the checkers demo and reveal the work one layer at a time.

![Image of an onion being peeled](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f3dfnqginskz34qs80rr.png)

!But first! Go hands-on with the SMEAR if you'd like (Chrome and Safari - desktop only) by selecting a front-row checker, then select a direction to move it in, then SMEAR to confirm:

{% codepen https://codepen.io/propjockey/pen/JjLKqGa default-tab=result theme-id=40148 %}

## Shrouded? What is the SMEAR hack doing?

The big reveal is right here in our first layer:

SMEAR is a drag-drop.

Code (CSS in this case) is on the right, and the user drags it into a contenteditable element (Style tag display block, contenteditable plaintext-only in this case) on the left.

We make the CSS and the style tag invisible to the user and put a fake call-to-action pseudo element in place for the user to seemingly-but-not-really interact with.

The last key at this layer is that the element with our code (shrouded markup) has `user-select: all;` so any interaction selects all of the code automatically and the drag action begins immediately.

Here's an unshrouded bare bones demo of what's happening:

{% codepen https://codepen.io/propjockey/pen/a6ed72d9d120789a8db8b625c25a5761 default-tab=css,result theme-id=40148 %}

## user-select: almost-all; How to change the code each generation

In order for the patch to be regenerated from our current state, we have to include markup that can programmatically represent any possible patch.

If we could somehow allow users to select pseudo element content, it would be much easier to generate the next generation's state from CSS. As it is now though, we have to omit the elements that don't apply to the next generation from a superset.

There are a few ways to omit text from the `user-select: all;`

`user-select: none;` is surprisingly not one of them because it still copies the seemingly unselected text. Probably a bug.

`display: none;` works but if your patch state is derived from an animation, you cannot set `display` to `none`. (Spoiler: we need to change our generated code from an animation hack)

`visibility: hidden;` works and can be set from an animation. Yay!

The state of our patched code will come down to math - like showing the win screen when the opposite player's total sum/count of pieces on the board is 0. So we need to cast an integer from a `calc()` to `visibility: hidden;`, which requires an animation.

`@keyframes markupToggler { from { visibility: hidden; } }`

and

`animation: markupToggler 1ms linear both var(--condition, 0);`

If `--condition` is 0, animation applies the `from` state, if `--condition` is 1, the animation stops applying the `from` state.

Here's what that looks like in action: note the content of our CSS patch regenerates every time we SMEAR and shows the opposite state.

{% codepen https://codepen.io/propjockey/pen/d384e548657fe8239584baf48c26d9c2 default-tab=css,result theme-id=40148 %}

This animation can be reused on any element, just set the `--condition` variable to a calc (or min/max/clamp/etc) that becomes 0 or 1 and you're good to go.

### Forcing new code to the bottom of the style element

There are a number of ways to prevent the user from dragging code into the middle of existing code (something which you may have done while interacting with the previous demo?) but the most reliable I've used (which could be trimmed down or done differently) is just adding this:

```css
style.shrouded-markup::before {
  content: "";
  display: block;
  margin-bottom: -999999vh;
}
style.shrouded-markup::after {
  content: "\A\A\A\A";
  display: block;
}
```

## Shrouding Reality

The only important gotcha here is that you'll want your fake call-to-action element to be on the ::before pseudo specifically, not ::after.

{% codepen https://codepen.io/propjockey/pen/06da280a735dc6846e21cb884fbe9ab0 default-tab=css,result theme-id=40148 %}

## What's next?

Maybe a follow up article if there's interest!

* How to `user-select: all;` on contenteditable elements (you can!)
and
* using a textarea and reset button (JS not required) to empty cache.

## The End!

If you think this idea/trick is neat, it's the kind of thing I do all the time! So please do consider following me here and [on twitter](https://twitter.com/Jane0ri) as well!

💜
// Jane Ori