“Can a browser render CSS live if it is fed into the page character by character?”
This incredibly useless and almost ignorant thought occurred to me as I was hovering over CodePen in bed (as we all do). I had happened upon a pen that for the life of me, I can no longer find. It was 404 page with an illustration of a developer sitting at his computer. It had a video element embedded on top of the computer screen playing a video of CSS being written. If you can find it, let me know. I’d like to thank whoever made it.
That pen unearthed a question burrowed deep in the recesses of my changelog. I often have thoughts in this setting, groggily lit by the blueish hue of my MacBook Pro. When I do, I either:
A. Discard it immediately before I waste a bunch of time.
This is the most common result—so much so that I have considered investing in a CodePenBedPan®. Or,
B. Most of these questions aren’t easily Google-able, so stay up entirely too late working them out.
This particular night was a classic B.
Surprise #1: It actually worked.
The basic mechanism is the
writeStyles method which takes three parameters.
interval. It checks to see if the
index is valid. If the
index is higher than the amount of characters in the
message, we are at the end of the string and it stops. If the
index is valid, it throws the character (character in
message with position
index) into a
writeStyleChar method injects the character into the
<style> elements. Later on, I would use this method to detect syntax (comments, declaration keys and values, etc.) using regex.
writeStyles method then continues by instantiating a
setTimeout which calls itself again creating a loop that will continue until there aren’t any more characters left.
Surprise #2: The result was oddly dynamic.
My expectation was that the closing
} would be required in order for the browser to use the code. I was wrong. In fact, all you need is a “somewhat” valid declaration.
So if the characters being written could influence time in the result, so could the timing inherent to the order of declarations. When a stylesheet is typically loaded, the cascade and specificity dictates which declarations are used and which are overridden (
color: blue; color: red; the color would be
red). You do not see the overridden (
blue) style. However, if you are writing the style one character at a time, this is not the case.
I realized that it is possible to override styles in sequence and display the sequence live on the page. In order to add a pause between declarations that override each other, I could simply add spaces to the string.
Surprise #3: The pen had a personality.
Once I figured out the functionality, I needed to explore the outer edges of the idea. As I started building out the pen in more detail, I started marking up the code with comments to keep things organized. As I did that, a story quickly began to unfold. From there it practically wrote itself. It was the most fun I’ve ever had coding.
(will not fully run while embedded in this post)
Surprise #4: You were all as delighted as I was.
I hesitated to make pen#PwLXXP public. You know that feeling, the one you get where you question everything in your life because you just shoveled your soul into something practically useless. The feeling that you are extremely deficient in all things dev; that posting your sweat-drenched code publicly would only destine it to falling back in the “picks” list with 3 likes. There is something precious about those pens while they remain private. The sky is the limit—they are the best idea you’ve ever had. So much creativity comes through iteration when you keep pens private (go pro already).
I hesitated to make pen#PwLXXP public, but I eventually did. The response was ridiculous. As ridiculous as someone making a self-authoring version of one of the Reddit links. More than anything, I was excited that it made a lot of people happy—even non-devs.
(will not fully run while embedded in this post)
I was able to create CSS and JS “modes” by using
` as a toggle character in the string. It is intercepted by the
# toggle CSS/JS on ` if which == "`" which = "" __js = !__js
If in CSS mode, it handles syntax highlighting and DOM injection in an improved, but similar way to pen#PwlXXP. If in JS mode, it waits for a trigger to create and run the script.
# Using JS if __js # running a command block. initiated with "~" if which == "~" && !openComment script_tag = createElement "script" # two lookback matches based on prior scenario prior_comment_match = /(?:\*\/([^\~]*))$/ prior_block_match = /([^~]*)$/ if _code_block.match(prior_comment_match) # get all js until prior comment script_tag.innerHTML = _code_block.match(prior_comment_match).replace("*/", "") else # get all js until prior ~ script_tag.innerHTML = _code_block.match(prior_block_match) # reset script area html $script_area.innerHTML = "" # append script to script area $script_area.appendChild script_tag # set char to current character char = which # add char to the pre element with potential syntax highlighting code_html = scriptSyntax($code_pre.innerHTML, char)
~ character. Whenever this gets fed into the method, it parses the
<script> element and throws it in the DOM.
If you haven’t, you should open up pen#JoVrdw full view and watch it unfold. Let’s just say it gets a bit...nested.
CodePen is a great place to experiment, learn, and be joyful. For those of you that hesitate to experiment, don’t. You are capable of anything you put your mind to. Don’t be afraid of failure, at the very least you’ll learn something.
For those of you that find experimentation to be a waste of time, it is not. I have learned too much while experimenting to let you write it off. It helps keep “that thing you do for a living” from becoming stagnant.
Find the joy in what we do, and share it with the rest of us.
I am Jake Albaugh and am going to write this bio in first person. These days, I write on CodePen because I care more about it and you than I do about my personal site. Read more articles via my CodePen blog page. View my work on my CodePen profile. If you’re a hip millennial, “get at me” on my twitter @jake_albaugh.