Now Reading
The advanced simplicity of my static web sites

The advanced simplicity of my static web sites

2023-08-13 10:08:14

It was the spring of 2014, over 9 years in the past, simply 6 months into my first yr of faculty, when my Pc Structure trainer stopped in the midst of an meeting train to inform us that Bitdefender is hiring juniors for Malware Researcher positions.

I had no thought what that’s, however boy, did it sound cool?…

I fondly keep in mind how at the moment we weren’t chasing excessive salaries and filtering jobs by programming languages and frameworks. We simply needed to study one thing.

As college students, we wanted cash as nicely after all, however after I received the job for 1750 lei (~€350), I instantly turned the richest 18 yr previous in my house city, so it wasn’t the highest precedence.

And we learnt a lot in 2 years.. obscure issues like AOP, a number of x86 meeting, reverse engineering methods which dumped us head first into languages like Java, .NET, ActionScript? (malware authors have been artistic).

However most of all, we did tons of Python scripting, and we beloved each minute of it. It was my first time getting acquainted with quick instruments like Sublime Text and FAR Manager. Coming from Notepad++ and Home windows Explorer, I felt like a mad hacker with the world at my fingertips.

I’m generally known as a macOS app dev these days, however 9 years in the past, I really began by writing petty Python scripts which spurred the obsessive love I’ve these days for clear accolade-free code and indentation primarily based languages.

What does all that must do with static web sites although?

# Pythonic HTML

Effectively, 5 years in the past, after I launched my first macOS app, I discovered myself needing to create a easy webpage to showcase the app and on the very least, present a strategy to obtain it.

And HTML I didn’t need to write. The XML like syntax is one thing I at all times dreaded, so overfilled with pointless </> symbols that make each writing and studying way more cumbersome. I needed Python syntax for HTML so I went in search of it.

I went via pug

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
doctype html
html
  head
    title Lunar - The defacto app for controlling monitor brightness
    meta(itemprop='description' content material='...')
    type.
      a.button {
        background: bisque;
        padding: 0.5rem 1rem;
        colour: black;
        border-radius: 0.5rem;
      }
      physique {
        show: flex;
        flex-path: column;
        align-gadgets: heart;
        justify-content material: heart;
        textual content-align: heart;
      }
  physique
    h1(type='colour: white; font: daring 3rem monospace') Lunar
    img(src='https://information.lunar.fyi/display-page.png' type='width: 80%')
    a.button(href='https://information.lunar.fyi/releases/Lunar.dmg') Obtain

fairly, however nonetheless wants () for attributes, and I nonetheless want accolades in CSS and JS

then haml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
!!!
%html{lang: "en"}
  %head
    %meta{content material: "textual content/html; charset=UTF-8", "http-equiv" => "Content material-Sort"}/
    %title Lunar - The defacto app for controlling monitor brightness
    %meta{content material: "...", itemprop: "description"}/
    :css
      a.button {
        background: bisque;
        padding: 0.5rem 1rem;
        colour: black;
        border-radius: 0.5rem;
      }
      physique {
        show: flex;
        flex-path: column;
        align-gadgets: heart;
        justify-content material: heart;
        textual content-align: heart;
      }
  %physique{type: "background: #2e2431; min-height: 90vh"}
    %h1{type: "colour: white; font: daring 3rem monospace"} Lunar
    %img{src: "https://information.lunar.fyi/display-page.png", type: "width: 80%"}/
    %a.button{href: "https://information.lunar.fyi/releases/Lunar.dmg"} Obtain

much more symbols: %, :, => and / for self-closing tags

…and ultimately stumbled upon Slim and its Python counterpart: Plim

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
doctype html
html lang="en"
  head
    title Lunar - The defacto app for controlling monitor brightness
    meta itemprop="description" content material="..."
    -stylus
      a.button
        background bisque
        padding 0.5rem 1rem
        colour black
        border-radius 0.5rem
      physique
        show flex
        flex-path column
        align-items heart
        justify-content material heart
        textual content-align heart

  physique type="background: #2e2431; min-height: 90vh"
    h1 type="colour: white; font: daring 3rem monospace" Lunar
    img src="https://information.lunar.fyi/show-web page.png" type="width: 80%"
    a.button href="https://information.lunar.fyi/releases/Lunar.dmg" Obtain

ahhh.. so clear!

Right here’s how that instance appears like if I must write it as HTML:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
<html>
    <head>
        <title>Lunar - The defacto app for controlling monitor brightness</title>
        <meta itemprop="description" content material="...">
        <type>
            a.button {
                background: bisque;
                padding: 0.5rem 1rem;
                colour: black;
                border-radius: 0.5rem;
            }

            physique {
                show: flex;
                flex-direction: column;
                align-items: heart;
                justify-content: heart;
                text-align: heart;
            }
        </type>
    </head>

    <physique>
        <h1 type="colour: white; font: daring 3rem monospace">Lunar</h1>
        <img src="https://information.lunar.fyi/display-page.png" type="width: 80%">
        <a class="button" href="https://information.lunar.fyi/releases/Lunar.dmg">Obtain</a>
    </physique>
</html>

not particulary onerous to learn, however writing would want a number of Shift-holding and repeating tags

The factor I like most about Plim, and why I caught with it, is that it may parse my different favourite symbol-hating languages with out extra configuration:

  • Python for abstracting away repeating constructions
  • Stylus for writing type tags
  • CoffeeScript for the script tags
  • Markdown for lengthy textual content content material

Right here’s a extra advanced instance to showcase the above options (might require sunglasses):

instance of writing a HDR web page part, much like the one on lunar.fyi

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
---!
  # use Python to generate the dynamic picture sizes for the srcset attr

  WIDTHS = [1920, 1280, 1024, 768, 640, 320]

  def srcset(picture, ext, page_fraction=1.0):
    return ','.be a part of(
      f'/img/{picture}/{width}.{ext} {width // page_fraction:.0f}w'
      for width in WIDTHS
    )

doctype html
html lang="en"
  head
    -stylus
      # use Stylus to do a readable media question that checks for broad colour gamut

      @media display and (colour-gamut: p3)
        @helps (-webkit-backdrop-filter: brightness(1.5))
          part#xdr
            -webkit-backdrop-filter: brightness(1)
            filter: brightness(1.5)

  physique
    part#xdr
      image
        supply sort="picture/webp" srcset=${srcset('xdr', 'webp', 0.3)}
        supply sort="picture/png" srcset=${srcset('xdr', 'png', 0.3)}

      -md
        # write markdown that renders as inline HTML

        Unlock the full brightness of your XDR show

        The **2021 MacBook Professional** and the **Professional Show XDR** function an extremely vibrant panel *(1600 nits!)*,
        however which is locked by macOS to a third of its potential *(500 nits...)*.

        Lunar can **take away the brightness lock** and enable you to enhance the brightness previous that restrict.

    -espresso
      # use CoffeeScript to detect if the browser may not assist HDR

      $ = doc.querySelector
      safari = /^((?!chrome|android).)*safari/i.take a look at navigator.userAgent

      window.onload = () ->
        if not safari
          $('#xdr')?.type.filter = "none"

And better of all, there isn’t a loopy toolchain, bundler or dependency hell concerned. No undertaking construction wanted, no configuration information. I can simply write a contact.plim file, compile it with plimc to a readable contact.html and have a /contact web page prepared!

In order that’s the way it went with my app: I wrote a easy index.plim, dropped it on Netlify and went on with my day.

# Complexity Price

  • 1 pip set up for getting the Plim CLI
  • 1 npm set up for getting stylus and coffeescript (non-compulsory)
  • 1 construct command for producing the HTML information

# Complicated simplicity

The app managed to get fairly a little bit of consideration, and whereas I stored creating it, for the subsequent 4 years the web site remained the identical heading – picture – obtain button single web page. It was solely a aspect undertaking in spite of everything.

Working for US corporations from Romania made good cash, nevertheless it was so tiring to get via 3h of video conferences every day, standups, syntax nitpicking in PR evaluation, SCRUM bullshit, JIRA, process writing, process assigning, estimating process time in T-shirt sizes??

In April 2021 I lastly received uninterested in writing ineffective code and promoting my time prefer it was some grain silo I may at all times fill again up with much more work…

I guess on developing my app further. Since my faculty days, I at all times selected the work that helps me study new ideas. In some unspecified time in the future I needed to perceive that I learnt sufficient and needed to begin sharing. This time I actually needed to write down software program that helped folks, and was prepared to spend my financial savings on it.

# Comically Stuffed Stylesheets

A extra full app additionally required a extra full presentation web site, however the styling was getting out of hand. You’ll suppose that with flexbox and grids, you’ll be able to simply write vanilla CSS as of late, however simply including a little bit of variation requires fixed leaping between the CSS and HTML information.

A presentation web page is often solely 10% HTML markup. The remainder is a ton of styling and duplicate textual content, so I needed to optimize my dev expertise for that.

There’s no “go to definition” on HTML .lessons or #ids as a result of their kinds will be outlined ✨wherever✨. So you need to Cmd-F like a madman and be very rigorous in your CSS construction.

The controversial however very intelligent answer to this was Tailwind CSS: a big assortment of brief predefined lessons that principally type simply the property they trace at.

For instance within the first code block I needed to write a non-reusable 5-line type to heart the physique contents.

1
2
3
4
5
6
7
physique {
  show: flex;
  flex-direction: column;
  align-items: heart;
  justify-content: heart;
  text-align: heart;
}

With Tailwind, I might have written the physique tag like so:

1
physique.flex.flex-col.justify-center.items-center.text-center

That may not appear to be a lot, some would argue that it’s even so much much less readable than the CSS one. Can’t I simply outline a .heart class that I can reuse?

Effectively, take into consideration a couple of issues:

  • this would possibly repeat on many sections of the web page, however with slight variations (what if I desire a centered row, or longer paragraphs of textual content aligned to the left)
  • responsive sections would possibly want to change format (e.g. vertical on cellular, horizontal on desktop) and media queries will rapidly blow up the type dimension
    • .md:flex-row.flex-col is what you’ll write in Tailwind
  • including darkish/mild mode assist is one more media question
    • .darkish:bg-white.bg-black appears easy sufficient
  • interactions like hover results, advanced shadows and filters like blur and brightness is syntax that’s usually forgotten
    • .shadow.hover:shadow-xl creates a carry off the web page impact on hover by making the shadow bigger
    • .blur.lively:blur-none un-blurs a component whenever you click on on it
  • selecting colors and reusing them wants a number of consideration
    • .bg-red-500.text-white units white textual content on saturated pink
    • red-100 is much less saturated, in the direction of white
    • red-900 is darker, in the direction of black

Certain, lengthy traces of lessons may not be so readable, however neither are lengthy information of CSS styling. A minimum of the Tailwind lessons are proper there at your fingertips, and you may change a -lg with a -xl to rapidly effective tune your type.

# Complexity Price:

  • 1 command added for constructing the minimal CSS from the lessons used
  • 1 npm set up for getting the Tailwind CLI
  • 1 config file for outlining customized colours, animations and so on. (non-compulsory)

# Responsive photos

So many individuals obsess over the dimensions of their JS or CSS, however fail to understand that the majority of their web page is unnecessarily giant and never nicely compressed photos.

After all, I used to be a kind of folks.

For years, my app’s web site had a screenshot of its window as an uncompressed PNG, loading slowly from high to backside and chugging the person’s bandwidth.

Old Lunar website

I had no thought, however screenshots and display recordings are more often than not as much as 10x bigger than their visually indistinguishable compressed counterparts.

I even wrote an app to repair that since I’m always sending screenshots to folks and was uninterested in ready for 5MB photos to add in speedy chats.

It’s known as Clop if you wish to test it out.

Sure, similar to that famous ransomware, it wasn’t that well-known on the time of naming the app.

I wanted much more photos to showcase the options of an app controlling monitor brightness and colours, so I had to enhance on this.

Delivering the smallest picture essential to the person is sort of a posh endeavour:

  1. Optimize the picture utilizing ImageOptim
  2. Resize it to suit a number of display sizes utilizing vipsthumbnail
  3. Work out what fraction of the web page width shall be occupied by the picture
  4. Write an acceptable srcset attribute to load the acceptable picture
  5. Non-obligatory: convert the picture to codecs like webp, avif or JPEG XL for smallest file dimension

I did a lot of that work manually up to now… fortunately these days I’ve imgproxy to do the encoding, optimization and resizing for me.

I simply have to write down the srcset, for which I outlined Plim and Python features to do the string wrangling for me.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
-def picture(img, ext='png', issue=0.4, mobile_factor=1)
    image
        -call img=${img} ext=${ext} issue=${issue} mobile_factor=${mobile_factor} self:sources
        img srcset=${srcset(img, ext, issue)}

-def sources(img, ext='png', issue=0.4, mobile_factor=1)
    supply sort="picture/avif" srcset=${srcset(img, ext, mobile_factor, convert_to="avif")} media="(max-width: 767px)"
    supply sort="picture/avif" srcset=${srcset(img, ext, issue, convert_to="avif")} media="(min-width: 768px)"

    supply sort="picture/webp" srcset=${srcset(img, ext, mobile_factor, convert_to="webp")} media="(max-width: 767px)"
    supply sort="picture/webp" srcset=${srcset(img, ext, issue, convert_to="webp")} media="(min-width: 768px)"

    supply sort="picture/${ext}" srcset=${srcset(img, ext, mobile_factor)} media="(max-width: 767px)"
    supply sort="picture/${ext}" srcset=${srcset(img, ext, issue)} media="(min-width: 768px)"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
WIDTHS = [1920, 1280, 1024, 768, 640, 320]

def imgurl(picture, width, ext="png", convert_to=""):
    conversion = f"@{convert_to}" if convert_to else ""
    return f"https://img.panaitiu.com/_/{width}/plain/https://lunar.fyi/img/{urllib.parse.quote(picture)}.{ext}{conversion}"


def srcset(picture, ext="png", issue=1.0, convert_to=""):
    return ",".be a part of(
        f"{imgurl(picture, width, ext, convert_to)} {width // issue:.0f}w"
        for width in WIDTHS
    )

# Complexity Price

  • 1 imgproxy server that should run someplace publicly obtainable, be stored alive and safe
  • some Python and Plim code for producing srcsets

# Scorching reloading

After 2 weeks of modifying the web page, Cmd-Tab to the browser, Cmd-R to refresh, I received actually uninterested in this routine.

I labored with Subsequent.js earlier than on Noiseblend and beloved how every file change routinely will get refreshed within the browser. Immediately and in-place as nicely, not a full web page refresh. I received the identical expertise after I labored with React Native.

There ought to be one thing for static pages too, I believed. Effectively it seems there may be, it’s known as LiveReload and I needed to slap my brow for not trying to find it sooner.

After putting in the browser extension, and working the livereloadx --static file watcher, I received my sizzling reloading dev expertise again.

Truly now that I give it some thought, Hugo has tremendous quick sizzling reloading, how does it accomplish that? Yep, seems Hugo uses LiveReload as nicely.

# Complexity Price

  • 1 extra command to run in 1 extra terminal panel, multiplex helps with that
  • 1 browser extension to put in and hope it’s not compromised or offered to an information thief
  • 1 npm set up for getting the livereloadx CLI

# Contact pages

After releasing the brand new app model, many issues have been damaged, expectedly.

Individuals tried to succeed in me in so some ways: Github points, private e mail, via the app licensing supplier, even Fb Messenger. I had no concept that together with an official means of contact can be so important.

And I had no thought how you can even do it. A contact kind wants, like, a server to POST to, proper? And that server must notify me in a roundabout way, after which I’ve to reply to the person in another means… sigh

I thought of these chat bubbles that a number of websites have, however I used them on Noiseblend and didn’t just like the expertise. Plus I dislike seeing them myself, they’re an eyesore and a nuisance obscuring web page content material and probably violating privateness.

After lengthy searches, undecided why it took so lengthy, I stumbled upon Formspark: a service that provides you a hyperlink to POST your kind to, they usually ship you an e mail with the shape contents. The e-mail will include the person e mail in ReplyTo so I can simply reply usually from my mail shopper.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
kind motion="https://submit-form.com/some-random-id"
    label for="identify" Identify
    enter#from hidden="true" identify="_email.from" sort="textual content"
    enter#identify identify="identify" placeholder="John Doe" required="" sort="textual content"

    label for="e mail" E-mail
    enter#e mail identify="e mail" placeholder="[email protected]" required="" sort="e mail"

    label for="topic" Topic
    enter#email-subject hidden="true" identify="_email.topic" sort="textual content"
    enter#topic identify="topic" placeholder="What's this message about?" required="" sort="textual content"

    label for="message" Message
    textarea#message identify="message" placeholder="One thing about our apps maybe" required="" sort="textual content" rows="6"

-coffee
    # Customized topic and identify: https://documentation.formspark.io/customization/notification-email.html#subject

    nameInput = doc.getElementById("identify")
    fromInput = doc.getElementById("from")
    nameInput.addEventListener 'enter', (occasion) -> fromInput.worth = occasion.goal.worth

    subjectInput = doc.getElementById("topic")
    emailSubjectInput = doc.getElementById("email-subject")
    subjectInput.addEventListener 'enter', (occasion) -> emailSubjectInput.worth = occasion.goal.worth

# Complexity Price

None, I suppose. I simply hope that the prolific however unique Formspark dev doesn’t die or get kidnapped or one thing.

# And also you name this “easy”?

It’s not. Actually. It’s loopy what I needed to undergo to get to a productive setup that matches my wants.

One may say I may have spent all that point on writing vanilla HTML, CSS and JS and I might have had the identical lead to the identical period of time. I agree, if time can be all that mattered.

However for some folks (like me), feeling productive, seeing how simple it’s to check my concepts and the way code appears to stream from my fingertips on the pace of thought, is what decides if I’ll ever end and publish one thing, or if I’ll lose my persistence and fallback to consolation zones.

Having to write down the identical boilerplate code again and again, fixed context switching between information, leaping again right into a undertaking after a couple of days and never understanding the place every part was in these thousand-lines information.. these are all detractors that can ultimately make me say ”f••ok this! not less than my day job brings cash”.

# Reusability

So many JS frameworks have been created within the identify of reusable elements, however all of them failed for me.

I imply certain, I can “npm set up” a React calendar, and I’m now “reusing” and never “reimplementing” the onerous work of somebody higher than me at calendar UIs. However simply attempt to stray away somewhat from the joyful path that the part creator envisioned, and you will see that it’s mind-bendingly onerous to bend the part to your particular wants.

You would possibly elevate a Github concern and the creator will add a couple of params so you’ll be able to customise that particular factor, however so will others with totally different and possibly clashing wants. Quickly sufficient, that part is said unwieldy and too advanced to make use of, the dev will say “f••k this! I’d rather do furniture” and another person will come out and say: right here’s the subsequent smartest thing in React calendar libraries, a lot easier to make use of than these behemoths!

I by no means had this aim in thoughts however unexpectedly, the above setup is generic sufficient that I used to be in a position to extract it right into a set of information for beginning a brand new web site. I can now duplicate that folder and begin altering site-specific bits to get a brand new web site.

Listed here are the web sites I’ve accomplished utilizing this methodology:

And the very best factor I keep in mind is that for every web site I revealed a working model, good trying sufficient, with a contact web page and small bandwidth necessities, in lower than a day.

How does this remedy the issue of straying away from the joyful path? Effectively, this isn’t an immutable library residing in node_modules, or a JS script on a CDN. It’s a set of information I can modify to the location’s wants.

There isn’t any excessive wall to leap (having to fork a library, determining its distinctive construct system and so on.) or want to stay to a selected construction. As soon as the folder is duplicated, it has its personal life.

For these , right here is the repo containing the present state of my templates: github.com/alin23/plim-website

I don’t suggest utilizing it, it’s potential that I’m the one one who finds it easy as a result of I do know what went into it. However for those who do, I’d like to hear your thoughts.

# Gatsby? Jekyll? Hugo?

Weirdly, this web site I’m writing on is just not made with Plim. In some unspecified time in the future I made a decision to start out a private web site, and I believed it most likely wants a blog-aware website builder.

On the time, I didn’t know that RSS is an simply templatable XML file, and that every one I would like for a weblog is to write down Markdown.

I keep in mind making an attempt Gatsby and never liking the JS ecosystem round it. Jekyll was my second alternative with Github Pages, however I believe I fumbled an excessive amount of with ruby and bundle to get it working and misplaced persistence.

Each issues stemmed from my lack of familiarity with their ecosystems, however my aim was to write down a weblog, not study Ruby and JS.

Hugo appeared a lot easier, and it was additionally written in Go and distributed as a standalone binary, which I at all times like for my instruments.

I marveled at Hugo’s pace, beloved the truth that it helps theming (though it’s not so simple as it sounds) and that it has a number of helpful stuff built-in like syntax highlighting, picture processing, RSS generator and so on. However it took me sooo lengthy to know its construction.

There are a lot of overseas phrases (to me) in Hugo: archetypes, taxonomies, shortcodes, partials, layouts, classes, sequence. Sadly, by the point I noticed that I don’t want the pliability that this construction supplies, I had already completed this web site and written my first article.

I additionally used a theme that makes use of the Tachyons CSS framework, for which I can by no means keep in mind the right class to use. I thought of rewriting the web site in Plim however changing every part to Tailwind or easy CSS would have been a number of work.

I ultimately began writing easy Markdown information for my notes, and have Caddy convert and serve them on the fly. Helps me write from my telephone and never must cope with Git and Hugo.

I nonetheless hold this for longform content material, the place a laptop computer is often wanted anyway.

Posted on:
August 8, 2023
Size:
18 minute learn, 3633 phrases
Classes:
web dev
Tags:
web dev static websites one page websites python plim hugo blog
See Additionally:

Source Link

What's Your Reaction?
Excited
0
Happy
0
In Love
0
Not Sure
0
Silly
0
View Comments (0)

Leave a Reply

Your email address will not be published.

2022 Blinking Robots.
WordPress by Doejo

Scroll To Top