I've been running soulreviews.net off a simple PHP site for a year. It's never caused me much of an issue, but I wasn't raring to improve it. Not many people I know use PHP, it's fairly infamous in my work environment, and it's just kind of boring. What does interest me though? Full-Stack lisp.
Reagent, the React Framework wrapped in Clojure and Ring to handle requests server-side. Sure, technically it's still JavaScript on the front-end and the JVM in the back, but all the code I'm actually writing is Clojure.
;;Front-end component
(defn blog-listing
"Blog Listing Component"
;;Sweet, sweet Destructuring of a JSON server response
[{:strs [title pub-date summary capsule-image-link link tags]}]
^{:key title}
;;<div> element with a .listing css class
[:div.listing
;;Adding element attributes is easy as a map
[:a {:href link} [:img {:src capsule-image-link}]]
[:div
[:time {:dateTime pub-date } pub-date]
[:a {:href link} [:h2 title]]
[:p summary]]])
;;Back-end server handler
(defn blog-list-handler
"Returns a list of blog metadata"
[_request]
(response
(json/read-str (slurp "/var/www/static/blogList.json"))))
I really like functional programming and working with data to build front-end components.
Studies have shown that practicing five minutes daily is better than practicing once a week for three hours.
What studies The Phoenix Project was referring to, I still have no idea, but it sounded like a nice idea so I tried applying this with my personal projects by making a little bit of progress each day:
On one hand, I think I'm internalizing the tortoise mentality that small, consistent progress will always lead to results, but on the other hand...
"Work expands so as to fill the time available for its completion."
With my game project and this website I set a completion date of about a month. Lo and behold, the projects were completed in the time I allocated to them. So either I have a godly talent for scheduling or I over-allocated and unconsciously put off additional work.
Granted, some trial and error is expected when you're learning a new framework, but reading though my work log, there were a few days were I did the bare minimum or I made bone headed mistakes by not being focused, like not allowing port 80 past the firewall.
Next project I'll see if I can't accelerate the work by making a public MVP in a week rather then a month.
It's up, your reading it, and I still want to work on it more.
The old site was terrible on phones. The buttons were too small, the comic didn't scale to screen size, and there were no comic navigation buttons at the top. If I succeeded in my mission, you can browse to this site with your phone and have a mostly seamless experience.
It's astonishing what a little CSS will do, just so long as you understand selectors, the difference between inline
and block
elements, and you have a collection good-looking sites to steal from. In that last case, I spent a lot of time in the Chrome debugger just exploring how bloggers I like laid out their sites. Really beats trying to stitch together something from scratch with only the CSS docs.
/*Float that nav bar to the side if there's space*/
/*Otherwise, rack it above the main content*/
@media (min-width: 1750px){
nav {
float: left;
position: fixed;
/*Don't you get larger then any of your buttons*/
max-width: 150px;
}
nav li {
/*Hold a row all to yourself*/
display: block;
/*Small margin around each button*/
margin: 0.5em;
}
}
In my professional career, I'll probably be tasked to build a more "professional" looking front-end with a CSS framework so the page matches the rest of the product line, which will most assuredly be Simple, Playful, Elegant, or Brutalist. Personal sites should be personal though and I associate Brutalism with daily commutes into DC, so I'll keep chugging along with this mess I've made.
The impetus for this entire re-design was a lost hackathon. Me and my colleges had built something insanely cool that solved a problem the hosting organization was having, with a working cloud deployment, and technology they were currently struggling with. We didn't get first place, we didn't get second, and they stopped ranking after third.
Absolute wipe out. And to rub salt in the wound, our competitors hadn't built anything cooler or more functional, but they had nice reactive front-ends.
Building something really cool isn't going to matter if the customer can't understand it, especially so if they're non-technical. So I've decided to learn from the mistake and get familiar with some front-end technology to wow future customers.
I'm happy to say that, thanks to Reagent, many of the Clojure libraries I was using, and all the wonderful people who contributed to Reagent's Lein template, 95% of my development work was focused on my core: UI and server-side API. I'm pretty confident if I had learned these tools before that hackathon, we would've been able to take first place and I'm excited for that opportunity in the future.
Lein templates are really useful if you know what your doing. They should not be used by novice Clojure developers who don't acknowledge the knowledge dept they've taken on. So of course, I did exactly that.
The problem all started when I wanted to include highlight.js in order to have syntax highlighting for code in my markdown blogs. Easy, just include a link to the CDN in a script
tag and highlight the code in a :component-did-update
hook:
;;server-side
(include-js
"//unpkg.com/@highlightjs/cdn-assets@11.4.0/highlight.min.js")
;;Interop on the front-end
(defn render-code [this]
(js/hljs.highlightAll (rdom/dom-node this)))
One small problem though, highlight.js supports many languages and they only include the top 30 or so in the CDN distribution to save on size. So highlight.js took it's best guess at my clojure code samples and thought I was writing Rust.
So I decided it was a good idea to try and follow this tutorial in order to add npm support by way of Webpack. Another small problem though, I had never heard of a deps.edn
or a deps.cljs.end
file. A couple hours of research and tinkering with my project.clj
and I decided what I actually wanted was to create a custom highlight.js package with just the language support I wanted and served by way of the :foreign-libs
, which met with an equal amount of success.
I could've avoided a lot of pain if I thought critically about the issue and asked "Hey, maybe they package the rest of the languages on CDNs as well!".
(include-js
"//unpkg.com/@highlightjs/cdn-assets@11.4.0/highlight.min.js"
;;Pull in clojure language support as well.
"//unpkg.com/@highlightjs/cdn-assets@11.4.0/languages/clojure.min.js")
What I will say is that project.clj
and .edn
files are very penetrable if your have the documentation on hand. They're just maps that define tasks and how to run them, even if those options are a little spread out across different libraries and tooling.
I'm definitely building my next project from the ground up with clj
. I know a few people I can tap for help on this and it seems worth it to avoid a situation like this in the future.
This one was lingering over me throughout the course of the project, enjoy this SPA version while you can, because the second I get a better handle on static site generation I'm remaking all of this.
Not to say the experience wasn't valuable, on the contrary, because Clojure is so data-centric I can copy my route definitions and hiccup forms into a static site generator with minimal effort.
However, a percentage of the audience I want to court here will have JavaScript disabled and will balk if they see a site asking to re-enable it. But more critically, why bother adding on a framework and that much more bandwidth to a site that is primarily informational? The last thing I want to waste is other people's time or make it inaccessible in low-connectivity areas.
One business use-case that could be argued is the webcomic long tail. That is, I want first-time visitors to be able to read 20-25 comics in one sitting to convert them to fans. The SPA facilitates this by intercepting redirects with accountant and only requesting information from the server it needs to generate the target page, theoretically making for a smoother browsing experiencing and allowing users to more easily binge my comics.
The first time I showed soulreviews.net to a friend in real-life, the first thing they did is recoil at the art. Next thing is that they tried to swipe right to go to the last comic, and then they tried again, and again, and again.
It was agonizing.
Quick-and-dirty usability tests often are.
If you do a quick perusal of popular web comic sites such as Kill 6 Billion Demons,MS Paint Adventures, and even phone-centric offerings like WebToons, you'll notice none of them have a swipe to go to the next/last page feature. An easy way to one-up the competition with something mobile users do on a daily basis? Sign me up!
So I got hammer.js up-and-running using this excellent tutorial and made it so swipe-right/swipe-left triggered a redirect. Great! But hammer sets touch-action: pan-y
and mobile users can no longer pinch to zoom or pan horizontally! I did try configuring hammer to set less restrictive touch-actions, but that borked it and calling .preventDefault()
functions wasn't fixing anything. It might be possible to just re-implement these features in hammer, but I suspect that might have different behaviors across platforms and I didn't want to play 20 questions with Apple, Microsoft, and Android phone/tablets.
In hindsight, the problem has mostly been solved by relative scaling of the navigation buttons and including them at both the top and bottom of the page, but I wouldn't be surprised if another quick-and-dirty UI test shows me more UX issues I didn't anticipate. Definitely doing that for the next hackathon.
I have a website built in full-stack lisp, a few experiments to run with my work process, and I've learned some skills that should prove useful next time a project needs a reactive front-end.
clj
tooling