<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Christoph Grabo</title>
    <description>The latest articles on DEV Community by Christoph Grabo (@asaaki).</description>
    <link>https://dev.to/asaaki</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2974%2F42da1afc-2085-428b-95b0-fe83b12e4f43.jpg</url>
      <title>DEV Community: Christoph Grabo</title>
      <link>https://dev.to/asaaki</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/asaaki"/>
    <language>en</language>
    <item>
      <title>Using git with multiple profiles and GPG+SSH keys</title>
      <dc:creator>Christoph Grabo</dc:creator>
      <pubDate>Sat, 03 Apr 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/asaaki/using-git-with-multiple-profiles-and-gpg-ssh-keys-ikg</link>
      <guid>https://dev.to/asaaki/using-git-with-multiple-profiles-and-gpg-ssh-keys-ikg</guid>
      <description>&lt;p&gt;I work across 2 computers and 3 OS, all with nearly same development setup. For work I need some special care for my git config, since I want/need access to personal repositories.&lt;/p&gt;

&lt;p&gt;The slightly more complex version control system configuration is due to the following requirements and expectations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;I want to use my personal (and main) email address for my personal GitHub projects.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;I want to use my &lt;strong&gt;work email&lt;/strong&gt; address for the &lt;strong&gt;workplace stuff.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;I want separate GPG keys for each email address.&lt;/li&gt;
&lt;li&gt;I want to sign my commits with the individual GPG keys for each of the addresses.&lt;/li&gt;
&lt;li&gt;I want separate SSH keys for each context (work, personal; but also for each computer).&lt;/li&gt;
&lt;li&gt;I want to use the individual SSH keys based on the project I work on.&lt;/li&gt;
&lt;li&gt;I want all of it happening as automatically as possible.&lt;/li&gt;
&lt;li&gt;I want to forget that it is there.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I know, I know, this looks like a huge list of requirements. But not only is this my personal preference, partially also my workplace has some strictler rules, especially when it comes to security.&lt;/p&gt;

&lt;p&gt;So, if you cannot get rid of the first point and do need also access to your personal persona on GitHub (or any other git based hosting platform for that matter), this article might help you to achieve it.&lt;/p&gt;

&lt;p&gt;By the way, this approach should work for both single and multiple GitHub accounts. Since GitHub makes it easy to keep using a single account while also being a member of an Enterprise organization, I haven't bothered testing it with a true multi-account configuration, but since you will use individual SSH and GPG keys for either way your computer won't really know the difference.&lt;/p&gt;

&lt;h2&gt;
  
  
  SSH configuration
&lt;/h2&gt;

&lt;p&gt;While &lt;code&gt;git&lt;/code&gt; does support the HTTPS transport, in most cases you will use the more preferred way of talking &lt;em&gt;git+ssh&lt;/em&gt; instead. So let's tackle this lower level first:&lt;/p&gt;

&lt;h3&gt;
  
  
  SSH Keys
&lt;/h3&gt;

&lt;p&gt;If you working with GitHub you want to generate keys with the latest and greatest recommended algorithms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh-keygen -t ed25519 -C "your_email@example.com"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you work on other platforms, please check first, which algorithms are supported there.&lt;/p&gt;

&lt;p&gt;In decreasing order of preference and security:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# prefer this if possible:
ssh-keygen -t ed25519

# this is still quite okay:
ssh-keygen -t ecdsa -b 521

# this only as a last resort option, should work everywhere:
ssh-keygen -t rsa -b 4096

# avoid this, please;
# also GitHub does not allow new keys with DSA anymore!
ssh-keygen -t dsa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More details about key generations at: &lt;a href="https://www.ssh.com/ssh/keygen/" rel="noopener noreferrer"&gt;https://www.ssh.com/ssh/keygen/&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;~/.ssh/config&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### github

### -- PERSONAL/MAIN ACCOUNT --

Host github.com
  Hostname github.com
  User &amp;lt;YOUR GITHUB USERNAME&amp;gt;
  IdentityFile ~/.ssh/&amp;lt;YOUR PERSONAL SSH KEY&amp;gt;

### -- WORK PERSONA/ACCOUNT --

Host github.com-work
  Hostname github.com
  User &amp;lt;YOUR GITHUB USERNAME&amp;gt;
  IdentityFile ~/.ssh/&amp;lt;YOUR WORK SSH KEY&amp;gt;

### general

Host *
  AddKeysToAgent yes
  IdentitiesOnly yes
  PreferredAuthentications publickey
  Compression yes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important note:&lt;/strong&gt; Do not change the order of the configuration.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For each parameter, the first obtained value will be used. The configuration files contain sections separated by ''Host'' specifications, and that section is only applied for hosts that match one of the patterns given in the specification. The matched host name is the one given on the command line.&lt;/p&gt;

&lt;p&gt;Since the first obtained value for each parameter is used, more host-specific declarations should be given near the beginning of the file, and general defaults at the end. &lt;cite&gt;&lt;a href="https://linux.die.net/man/5/ssh_config" rel="noopener noreferrer"&gt;ssh_config(5) manual&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since we will use two different hosts, we must repeat the &lt;code&gt;Hostname&lt;/code&gt; line, but also want to specify &lt;code&gt;User&lt;/code&gt; and &lt;code&gt;IdentityFile&lt;/code&gt; specifically. If you use your ssh config only for git and also have only a single user name, you could move that config line into the generic section in the bottom. Though I still prefer the explicit repetition for each specific Host block.&lt;/p&gt;

&lt;p&gt;At this point you might wonder »How is &lt;em&gt;github.com-work&lt;/em&gt; supposed to function?« Hold that thought, we will come back to it later.&lt;/p&gt;

&lt;h2&gt;
  
  
  GPG key management
&lt;/h2&gt;

&lt;p&gt;You want to use GPG for signing commits. Your workplace might not require it (yet), but if you have any level of trust issues, or just want to be sure that a commit was made by you and be able to prove it, use commit signing.&lt;/p&gt;

&lt;p&gt;Interestingly GitHub does not recommend you to use a more modern algorithm and &lt;a href="https://docs.github.com/en/github/authenticating-to-github/generating-a-new-gpg-key" rel="noopener noreferrer"&gt;requires you to pick RSA.&lt;/a&gt; A bit sad, but RSA with 4096 bits seems to be still fine for this purpose.&lt;/p&gt;

&lt;p&gt;After you have created your keys, you should grab the IDs for them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gpg --list-secret-keys --keyid-format LONG

# I use shorter IDs and git doesn't seem to struggle
gpg --list-secret-keys --keyid-format SHORT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/home/asaaki/.gnupg/pubring.kbx
-------------------------------
sec rsa4096/D73D7242 2021-02-14 [SC]
      AE93078BDC15BF6A84767DBBA3CBBB61D73D7242
uid [ultimate] TEST KEY &amp;lt;test-key@example.com&amp;gt;
ssb rsa4096/57047776 2021-02-14 [E]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ID is the part after the &lt;code&gt;rsa4096/&lt;/code&gt; from the &lt;code&gt;sec&lt;/code&gt; line: &lt;code&gt;D73D7242&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In the LONG variant it just used more characters from the whole fingerprint (which you can completely see in the line between sec and uid).&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  git shenanigans
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;git&lt;/code&gt; command line interface (CLI) improved a lot over the years. I remember that I used a custom &lt;code&gt;GI_SSH=…&lt;/code&gt; wrapper in my shell environments to make this whole host/key mapping possible, but thanks to the power of git configs this is a thing of the past.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;~/.gitconfig&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[init]
  defaultBranch = main

[commit]
  gpgsign = true

# other sections cut for brevity; add the following to the bottom:

[user]
  name = Your Name
  useConfigOnly = true

# optional (if you sometimes work outside of your regular directories)
[include]
  path = ~/.gitconfig.personal

[includeIf "gitdir:~/Development/Personal/"]
  path = ~/.gitconfig.personal

[includeIf "gitdir:~/Development/Work/"]
  path = ~/.gitconfig.work
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;~/.gitconfig.personal&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[user]
  email = your.personal@email.here
  signingkey = &amp;lt;PERSONAL GPG SIGNING KEY ID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;~/.gitconfig.work&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[user]
  email = your.work@email.here
  signingkey = &amp;lt;WORK GPG SIGNING KEY ID&amp;gt;

[url "git@github.com-work"]
  insteadOf = git@github.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;include&lt;/code&gt; and &lt;code&gt;includeIf&lt;/code&gt; are the key components to separate out specific configurations based on where you are on your system.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;include&lt;/code&gt; is always pulled in, this is useful if you really want to separate out parts of your main &lt;code&gt;.gitconfig&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you only work in specific directories like &lt;code&gt;~/Development/Personal/&lt;/code&gt; and &lt;code&gt;~/Development/Work/&lt;/code&gt; — and this means where you also need to commit and push — then you could remove the general &lt;code&gt;[include]&lt;/code&gt; section entirely. I keep it around, because I might have checked out a repository somewhere else and don't want to move it for a commit.&lt;/p&gt;

&lt;p&gt;There's probably a better way to organize this, but the above has served me well for quite some time now.&lt;/p&gt;

&lt;p&gt;I recently added the global &lt;code&gt;[user]&lt;/code&gt; section for setting &lt;code&gt;useConfigOnly = true&lt;/code&gt; and the name, since I only have one. &lt;code&gt;useConfigOnly&lt;/code&gt; prevents git from guessing name and email and forces it to read it from the configuration files. If the email would be missing, git will complain the next time you try to commit or push. And you will know that something is broken in the configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important note:&lt;/strong&gt; The trailing slashes (&lt;code&gt;/&lt;/code&gt;) on the &lt;code&gt;[includeIf …]&lt;/code&gt; lines are very important. If you forget them, then git would try to match only this very specific folder and ignore it for any folder within it. More details about &lt;strong&gt;conditional includes&lt;/strong&gt; in the &lt;a href="https://git-scm.com/docs/git-config#_conditional_includes" rel="noopener noreferrer"&gt;git documentation&lt;/a&gt;. &lt;em&gt;(I totally missed that you can use them even for branches, too.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;signingkey&lt;/code&gt; values should be set appropriately based on the IDs you have noted from the &lt;a href="https://markentier.tech/posts/2021/02/github-with-multiple-profiles-gpg-ssh-keys/#gpg-key-management" rel="noopener noreferrer"&gt;previous section&lt;/a&gt; of this article. Now when you commit anything git will use the correct key based on where the repository directory lives.&lt;/p&gt;

&lt;p&gt;To automatically enforce the commit signing, use &lt;code&gt;commit.gpgsign&lt;/code&gt; set to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Remember the question you had earlier about the weird looking Host configuration in SSH? The &lt;code&gt;[url "git@github.com-work"]&lt;/code&gt; section is the counterpart making it work, because git will do the translation when you are in your company's repos.&lt;/p&gt;

&lt;p&gt;What will happen is the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You are in a &lt;strong&gt;work&lt;/strong&gt; related repository.&lt;/li&gt;
&lt;li&gt;You want to &lt;em&gt;fetch/pull&lt;/em&gt; the latest changes or &lt;em&gt;push&lt;/em&gt; your local state to the origin.&lt;/li&gt;
&lt;li&gt;Since git will load the &lt;strong&gt;work&lt;/strong&gt; config, it replaces the regular URLs having &lt;code&gt;git@github.com&lt;/code&gt; in it with the value of the &lt;code&gt;[url …]&lt;/code&gt;, here &lt;code&gt;git@github.com-work&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;git reaches one level down and uses ssh for the communication.&lt;/li&gt;
&lt;li&gt;SSH sees a &lt;code&gt;github.com-work&lt;/code&gt; host and tries to find all matching configurations for that, including the exact one we have &lt;a href="https://markentier.tech/posts/2021/02/github-with-multiple-profiles-gpg-ssh-keys/#ssh-config" rel="noopener noreferrer"&gt;defined above&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;SSH will ignore our personal configuration, because the regular &lt;code&gt;github.com&lt;/code&gt; does not match anymore.&lt;/li&gt;
&lt;li&gt;SSH picks up your &lt;strong&gt;work&lt;/strong&gt; identity file for authentication with GitHub's server.&lt;/li&gt;
&lt;li&gt;SSH will also use the actual &lt;code&gt;Hostname&lt;/code&gt; instead of &lt;code&gt;Host&lt;/code&gt; (translating it back again).&lt;/li&gt;
&lt;li&gt;Everyone is happy. \o/&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;What's with this &lt;code&gt;[init]&lt;/code&gt; block you haven't mentioned before?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Since Git 2.28 you can set a name for the default branch when creating a new repository. Be a good citizen and avoid offensive and negatively connotated terms. The wider developer community seems to like "main" as a good replacement for the previous default name. You can read more about &lt;a href="https://github.com/github/renaming" rel="noopener noreferrer"&gt;renaming existing default brances&lt;/a&gt; on GitHub.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus
&lt;/h3&gt;

&lt;p&gt;To quickly check which config is used execute the following command in a repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git remote -v
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The URL should provide you a quick hint if the correct profile is used.&lt;/p&gt;

&lt;p&gt;Alternatively you can also run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git config --show-origin --get user.email
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will also print where the final value was retrieved from.&lt;/p&gt;




&lt;p&gt;Git came a long way and I'm very glad and happy that we can have such setup without a lot of manual tinkering and workarounds. Gone are all my custom scripts, snippets, and &lt;code&gt;.envrc&lt;/code&gt;'s for that purpose, which were also not completely platform agnostic.&lt;/p&gt;

</description>
      <category>git</category>
      <category>gpg</category>
      <category>ssh</category>
      <category>howto</category>
    </item>
    <item>
      <title>Rust/Wasm on AWS Lambda@Edge</title>
      <dc:creator>Christoph Grabo</dc:creator>
      <pubDate>Tue, 16 Mar 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/asaaki/rust-wasm-on-aws-lambda-edge-37mp</link>
      <guid>https://dev.to/asaaki/rust-wasm-on-aws-lambda-edge-37mp</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;code&gt;tl;dr:&lt;/code&gt; Check out the &lt;a href="https://github.com/asaaki/rust-wasm-on-lambda-edge" rel="noopener noreferrer"&gt;repo&lt;/a&gt; and give it a spin for yourself.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I sat down yet again and took some of my learnings into a repository, so we have a starting point.&lt;/p&gt;

&lt;p&gt;As of today (January 2021) AWS only offers two languages for Lambda@Edge: &lt;code&gt;Python&lt;/code&gt; and &lt;code&gt;Node.js&lt;/code&gt;. Both are available in relatively decent versions, too.&lt;/p&gt;

&lt;p&gt;Since I have never bothered with Python too much in my life, but have my fair share of experience with JavaScript (JS) and node, I stuck to the latter as the trampoline environment for the setup. Also I know that it comes with WebAssembly support out of the box, which wouldn't be the case for Python anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what is it gonna be anyway?
&lt;/h2&gt;

&lt;p&gt;As you've maybe guessed the project will be some Rust, which gets compiled down to WebAssembly. This artefact then can be loaded and executed in the node environment.&lt;/p&gt;

&lt;p&gt;A word about performance: I have not evaluated a "JS vs Wasm" comparison, and it's also not part of this exercise. There have been people and article in the past vouching for one side or another, all with their own benchmarks. So I won't bother you with that and advise you to take your own measurements.&lt;/p&gt;

&lt;p&gt;WebAssembly will not beat all JavaScript code, especially very fine-tuned one. V8 (the underlying JavaScript engine for both Chrome and Node.js) is a very performant beast and comes with &lt;em&gt;just in time&lt;/em&gt; (optimizing) compilation for further boosts.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Rust code in Wasm clothing&lt;/em&gt; can give you probably certain garantuees you miss from JavaScript, but again you have to evaluate if the benefits are worth the effort.&lt;/p&gt;

&lt;p&gt;Potentially you might also consider to switch to Python as your runtime instead. At least that language should have real integers as far as I know. 😉&lt;/p&gt;

&lt;p&gt;No doubt you can build and deliver very fast and also safe programs with Rust/WebAssembly. Especially if you need some specific algorithms and computations where JS/node might not be the best and you would resort to some (probably C-based) native libraries anyway.&lt;/p&gt;

&lt;p&gt;There are only a few issues with that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You have not full control of the execution environment of your edge functions. Sure, you can introspect with a test function what you're dealing with, but how sure will you really be, that the environment on CloudFront does provide the exact same system and system dependencies as your local development environment (or the non-edge Lambda environment for that matter)? AWS has a track record of not providing you with reproducible local environments. In fact, it looks like they get away with it even further, since the announcement for containerization support for regular AWS Lambda. &lt;em&gt;People, who know me, also know that I'm not a big fan of big docker images, but I'm afraid that's what we will see now happening there. I hope AWS promotes good containerization guidelines to prevent that waste-of-storage mess. Furthermore I really don't want to see docker on the edge for that reason. One can just hope, right?&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You work in a very constrained environment. Check the &lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-requirements-limits.html#lambda-requirements-see-limits" rel="noopener noreferrer"&gt;current limits&lt;/a&gt; for Lambda@Edge: the zipped code can use up to 50MB on the origin side and &lt;strong&gt;only 1MB max&lt;/strong&gt; if it shall be deployed for the viewer side. Of course, this is usually still plenty of spaces for most use cases, packaging up plain JS results in very small archives. But once you take the first issue into consideration, then this could actually become another problem for you.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The size restriction can be mitigated for JS-only code pretty easily by bundling the code with &lt;a href="https://webpack.js.org/" rel="noopener noreferrer"&gt;WebPack&lt;/a&gt;, &lt;a href="https://parceljs.org/" rel="noopener noreferrer"&gt;Parcel&lt;/a&gt;, or &lt;a href="https://rollupjs.org/" rel="noopener noreferrer"&gt;Rollup&lt;/a&gt;. General advise is anyway to never deploy the unoptimized package especially when you want to push it to the edge. The &lt;code&gt;node_modules&lt;/code&gt; folder grows very big, can still have quite some bloat even after an &lt;a href="https://docs.npmjs.com/cli/v6/commands/npm-prune" rel="noopener noreferrer"&gt;&lt;code&gt;npm prune --production&lt;/code&gt;&lt;/a&gt;, because it only looks at packages, but not the content of them. &lt;em&gt;Yep, my minimalism brain kicked in again.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The system dependency problem can only be solved by using solely pure JavaScript libraries and packages. That might work for a while, but eventually some use case might demand a non-JS solution (either a native library or some executable).&lt;/p&gt;

&lt;p&gt;For example let's say you want to build an image transformation function and want to use &lt;code&gt;sharp&lt;/code&gt;, a very well-known package in the JS ecosystem, then you already end up with around 37 MiB of data in your node_modules folder alone. Zipped up it's still around 13 MiB. That might be enough for you to run it as a trigger on the origin side of your CloudFront distribution; it's just about showing you how quickly a node project can grow.&lt;/p&gt;

&lt;h2&gt;
  
  
  If size and dependency management are not an issue
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Maybe you love Rust (or any frontend language capable of being compiled to WebAssembly).&lt;/li&gt;
&lt;li&gt;Maybe you love WebAssembly.&lt;/li&gt;
&lt;li&gt;Maybe you do not have good JavaScript/Node.js expertise in-house.&lt;/li&gt;
&lt;li&gt;Maybe you want to build your product with better safety.&lt;/li&gt;
&lt;li&gt;Maybe it should be more robust, too.&lt;/li&gt;
&lt;li&gt;Maybe you want to show AWS, that we need more than just Python and Node.js on the edge.&lt;/li&gt;
&lt;li&gt;Maybe you have some other valid reason to escape that limiting cage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whatever your reasons are, I hear you.&lt;/p&gt;

&lt;p&gt;AWS is improving on one side, but also loosing it on another. When it comes to CDNs (Content Delivery Networks) and Edge Computing, the competition is now sprinting ahead of AWS.&lt;/p&gt;

&lt;p&gt;I cannot say a lot about Fastly's offering, it's mostly behind some beta doors, and mere mortals like myself are not allowed to peek. They have their fastlylabs, but that's for experimentation, not the final offering. So I don't even bother to check it out.&lt;/p&gt;

&lt;p&gt;I can tell a bit more about &lt;strong&gt;Cloudflare&lt;/strong&gt; though, because their edge computing offering is available and affordable to small businesses and individuals (even free in most cases). Check out &lt;a href="https://workers.cloudflare.com/" rel="noopener noreferrer"&gt;Workers&lt;/a&gt;, really do! I have already played around with Workers and KV storage, it's a quite pleasant experience. I might write about a setup for them in the future as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's get started
&lt;/h2&gt;

&lt;p&gt;GitHub repository to follow along:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/asaaki/rust-wasm-on-lambda-edge" rel="noopener noreferrer"&gt;https://github.com/asaaki/rust-wasm-on-lambda-edge&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ tree -L 1 -a -I .git # output truncated for brevity

.
├── .github - GitHub Actions workflow and dependabot
├── .gitignore
├── Makefile - very convenient make targets
├── README.md
├── fixtures - simple test fixtures and script
├── node - Node.js wrapper
├── rust - Big Business Here!
└── &amp;lt;and some more …&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Ingredients
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Makefile for project level management&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt; (TS) for the Node.js part&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/@types/aws-lambda" rel="noopener noreferrer"&gt;Type definitions&lt;/a&gt; for AWS Lambda&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://rollupjs.org/" rel="noopener noreferrer"&gt;Rollup&lt;/a&gt; as the bundler&lt;/li&gt;
&lt;li&gt;Rust for the, well, Rust part&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://rustwasm.github.io/wasm-pack/" rel="noopener noreferrer"&gt;wasm-pack&lt;/a&gt; for WebAssembly building&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;zip&lt;/code&gt; to package up the function for upload&lt;/li&gt;
&lt;li&gt;Example fixtures and code to have a very quick and dirty request test&lt;/li&gt;
&lt;li&gt;GitHub Actions workflow for continuous integration (CI) purposes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On your machine you need to install Rust, node, wasm-pack, and zip, if not present yet. The workflow for GitHub Actions has that already sorted out for you.&lt;/p&gt;

&lt;p&gt;This article won't give you steps to get your local development environment set up, please use a search engine of your choice and look up how to do it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Node.js wrapper
&lt;/h2&gt;

&lt;p&gt;I adopted a rollup based approach, since it's quite easy to get configured and also something we use at work. I always found webpack a little bit too cumbersome, and parcel is just yet another new kid on the block. I'm pretty sure you can adjust the project to your needs. All we need here is: compile TS to JS and bundle up everything into a single JS file. In the past I found the WebAssembly dependency management very tricky, in the end I used a plain "move the .wasm file into the final bundle" approach, which just works fine, because I did not want to inline the WebAssembly code (as most plugins try). Maybe you have a smarter solution for that, please open a pull-request in the &lt;a href="https://github.com/asaaki/rust-wasm-on-lambda-edge" rel="noopener noreferrer"&gt;repo&lt;/a&gt;. Just keep in mind: wasm-bindgen creates already a pretty decent module loader, so there is no need to work around that, but I fail to get any of these bundlers to move the wasm files along with it into the bundle output directory.&lt;/p&gt;

&lt;p&gt;I use TypeScript here, because it gives you some nice support during development. Also the &lt;a href="https://www.npmjs.com/package/@types/aws-lambda" rel="noopener noreferrer"&gt;aws-lambda type definitions&lt;/a&gt; were useful to create the Rust structs and adjust the serialization. (AWS is actually very strict about the JSON you pass around, &lt;code&gt;"something": null&lt;/code&gt; for absent data does not work, either it is a completely required field with a strict type like a string, or it should not be in the JSON at all).&lt;/p&gt;

&lt;p&gt;In general for any bigger frontend or node backend project I would recommend to use TS nowadays. While not every of your dependencies might come with type definitions, at least within your own codebase you can enforce strict rules and type checks.&lt;/p&gt;

&lt;p&gt;To make the node wrapper as slim as possible, we pass the event and context data directly into the WebAssembly and return whatever it returns.&lt;/p&gt;

&lt;p&gt;Btw if you do return a specific struct instead of a generic &lt;code&gt;JsValue&lt;/code&gt;, then TS checks will also kick in and use the auto-generated type definitions from the wasm-bindgen process.&lt;/p&gt;

&lt;p&gt;For a quick baseline test and project I did not go down that road yet, as it would require to replicate all the TS specific stuff in the Rust code (wasm-bindgen cannot do everything fully automated yet). This is a great opportunity to create a utility crate for that, basically like &lt;a href="https://github.com/tzaeru/rs2ts" rel="noopener noreferrer"&gt;rs2ts&lt;/a&gt; but in reverse direction. I wish &lt;a href="https://github.com/LegNeato/aws-lambda-events" rel="noopener noreferrer"&gt;aws-lambda-events&lt;/a&gt; already had those CloudFront events in it, but sadly they don't.&lt;/p&gt;

&lt;p&gt;Yet also be aware of certain type limitations, read on to learn more about them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rust business logic
&lt;/h2&gt;

&lt;p&gt;The Rust code is also nothing special so far.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/lib.rs

#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

mod types;

use std::panic;
use types::cloudfront as cf;
use wasm_bindgen::{intern, prelude::*};
use web_sys::console;

type JsValueResult = Result&amp;lt;JsValue, JsValue&amp;gt;;

#[wasm_bindgen(start, final)]
pub fn start() {
    panic::set_hook(Box::new(console_error_panic_hook::hook));
    console::log_1(&amp;amp;intern("(wasm module start)").into());
}

#[wasm_bindgen(final)]
pub async fn handler(event: JsValue, _context: JsValue) -&amp;gt; JsValueResult {
    console::log_1(&amp;amp;intern("(wasm handler request call)").into());
    let request = cf::Event::request_from_event(event)?;

    // TODO: Fancy biz logic here ...

    request.to_js()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;small&gt;&lt;em&gt;Note: The displayed code might not be up-to-date with the repository version.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;There is one function (&lt;code&gt;start&lt;/code&gt;) which is triggered when the Wasm module gets loaded. You can use it to set up some internal state if needed. We only used it here for configuring the panic handler; whenever an unrecoverable error happens, it gets logged via &lt;code&gt;console.error&lt;/code&gt;, helps immensly with debugging. And as we do console logging anyway, there shouldn't be any significant overhead for that part. The compilation output will probably a bit bigger because it needs to store more information for the better panic output.&lt;/p&gt;

&lt;p&gt;The other—probably way more interesting—function is &lt;code&gt;handler&lt;/code&gt;, which takes the inputs from the JS side, does … a lot of nothing, and returns a request JSON blob for CloudFront to deal with.&lt;/p&gt;

&lt;p&gt;Currently the machinery reads the arbitrary &lt;code&gt;JsValue&lt;/code&gt; and tries to deserialize it into a struct, so we can deal with it in code. This is definitely not the most efficient way of doing it, but the conversions in and out really avoid some current existing pain points.&lt;/p&gt;

&lt;p&gt;For example wasm-bindgen has not a great story around Rust enums, for now only very simple C-style enums are allowed. Meaning: for our CloudFront (CF) event data, which can be more strictly typed into either a CF request or response event, this does not play well with Rust's richer enums, as we cannot convince wasm-bindgen to use them. There is an &lt;a href="https://github.com/rustwasm/wasm-bindgen/issues/2407" rel="noopener noreferrer"&gt;open issue&lt;/a&gt; around this topic, but it was created just recently and thus no work has been done yet. Similarly Rust's &lt;code&gt;Vec&lt;/code&gt; is also not fully supported yet (see &lt;a href="https://github.com/rustwasm/wasm-bindgen/issues/111" rel="noopener noreferrer"&gt;issue 111&lt;/a&gt;), which might be the even bigger issue for some of us.&lt;/p&gt;

&lt;p&gt;Workarounds can be a lot of Options and serialization skips, as I do internally anyway.&lt;/p&gt;

&lt;p&gt;Some transformation overhead can be addressed by using &lt;a href="https://github.com/cloudflare/serde-wasm-bindgen" rel="noopener noreferrer"&gt;serde-wasm-bindgen&lt;/a&gt;, but in my example repo I'll use it only for the input side (deserialization). On serialization a collection like HashMap or BTreeMap gets turned into an ES2015 Map, which is unfortunated as well, because they cannot be JSON stringified.&lt;/p&gt;

&lt;p&gt;As you can see, currently there are trade-offs to be made in all corners, but that shouldn't stop us to explore further.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In the current state of the project I have provided pretty strict structs and enum for the CloudFront event data, it even surpasses now the TypeScript interfaces, which makes my point from the previous section pretty obsolete now. I still wish it was easier to autogenerate Rust types from TS definitions. The only good thing about CloudFront related data is, that it won't change that much … if at all. Some APIs in AWS have been stable for years now, so a "write once, use forever" approach might be sufficient.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;Read about my poor man's &lt;a href="https://markentier.tech/posts/2021/01/rust-wasm-on-aws-lambda-edge/#performance" rel="noopener noreferrer"&gt;performance testing of AWS Lambda@Egde functions&lt;/a&gt; on my blog. The tl;dr is that it's equally fast compared to a regular node.js setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This is all great news: you can run WebAssembly on AWS Lambda@Edge without a noticable performance penalty. Now write your Rust code and run it on the edge.&lt;/p&gt;

&lt;p&gt;Of course I do hope that in the future this will become more native. There's a lot of development happening in the WebAssembly space.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>webassembly</category>
      <category>lambda</category>
      <category>aws</category>
    </item>
    <item>
      <title>Shrinking SVG code: ❮path❯ not always bigger</title>
      <dc:creator>Christoph Grabo</dc:creator>
      <pubDate>Thu, 15 Oct 2020 15:52:42 +0000</pubDate>
      <link>https://dev.to/asaaki/shrinking-svg-code-path-not-always-bigger-3o4e</link>
      <guid>https://dev.to/asaaki/shrinking-svg-code-path-not-always-bigger-3o4e</guid>
      <description>&lt;p&gt;I love vector graphics. I love SVG. And I love a good session of byte squeezing.&lt;/p&gt;

&lt;p&gt;When I was triggered by &lt;a href="https://medium.com/markentier-tech/mediums-new-logo-what-a-strange-looking-thing-d2ddad63fa97" rel="noopener noreferrer"&gt;Medium's new logo&lt;/a&gt;, I played around with their SVG source for the symbol icon, what they call "Unfinished Ellipses" — let's not go into the "beauty" of it … &lt;small&gt;&lt;em&gt;(to be honest I don't like it; it does not look very inspiring to me)&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;This post is more about some tiny revelation I had during my &lt;a href="https://markentier.tech/posts/2020/10/medium-icon-svg/" rel="noopener noreferrer"&gt;SVG icon optimization&lt;/a&gt; session earlier today.&lt;/p&gt;

&lt;p&gt;Most of the time the &lt;code&gt;&amp;lt;path&amp;gt;&lt;/code&gt; element is used for more or less complex graphics. But in the case of Medium's symbol, it was clear they could have used very simple shapes (aka primitives) like &lt;code&gt;&amp;lt;circle&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;ellipse&amp;gt;&lt;/code&gt; (yes, Medium, that is a thing! :P), and others.&lt;/p&gt;

&lt;p&gt;And most of the time the final code with such primitives should be much shorter, as a &lt;code&gt;circle&lt;/code&gt; or &lt;code&gt;ellipse&lt;/code&gt; can be described in a very concise fashion, while a &lt;code&gt;path&lt;/code&gt; only knows how to draw straight lines, curves, and arcs. All of them involve usually more data than the primitive version. &lt;/p&gt;

&lt;h2&gt;
  
  
  Example: circle vs path
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- simple shape: a circle --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;circle&lt;/span&gt; &lt;span class="na"&gt;cx=&lt;/span&gt;&lt;span class="s"&gt;"500"&lt;/span&gt; &lt;span class="na"&gt;cy=&lt;/span&gt;&lt;span class="s"&gt;"500"&lt;/span&gt; &lt;span class="na"&gt;r=&lt;/span&gt;&lt;span class="s"&gt;"500"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- converted to path --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M1000 500a500 500 0 01-500 500A500 500 0 010 500 500 500 0
         01500 0a500 500 0 01500 500z"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So all is good and we can go home, right?&lt;/p&gt;

&lt;p&gt;Well, not so fast.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;TIL&lt;/code&gt; There are exceptions when the code of your primitive can be bigger than a complete path definition. And I think the &lt;code&gt;line&lt;/code&gt; element will be always heavier compared to its path counterpart.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: line vs path
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- line primitive: --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;line&lt;/span&gt; &lt;span class="na"&gt;x1=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;y1=&lt;/span&gt;&lt;span class="s"&gt;"80"&lt;/span&gt; &lt;span class="na"&gt;x2=&lt;/span&gt;&lt;span class="s"&gt;"100"&lt;/span&gt; &lt;span class="na"&gt;y2=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- path equivalent: --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt;  &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M0 80l100-60"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;&amp;lt;!-- 16 chr --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A single line would not make such a difference, but with many of them that can add up easily. And given that you usually combine many lines into a single path you will start saving even more.&lt;/p&gt;

&lt;p&gt;So, unless you draw only a few circles, ellipses, and rectangles you will probably be better off with a single or combined &lt;code&gt;path&lt;/code&gt; element.&lt;/p&gt;

&lt;p&gt;Oh, and a very easy way to optimize your SVG code is the site &lt;a href="https://jakearchibald.github.io/svgomg/" rel="noopener noreferrer"&gt;SVGOMG&lt;/a&gt;. I know I can install &lt;code&gt;svgo&lt;/code&gt;, but for a quick and dirty pass I always visit that page and paste my source and see how much it can reduce.&lt;/p&gt;

&lt;p&gt;Final note: &lt;strong&gt;Always verify your assumptions.&lt;/strong&gt; I was not 100 % sure about the overhead or savings of the primitive shapes, I'm glad I ran tests to get some proof. And don't be afraid if your experiments tells that you were wrong. That's life. 🤷🏻‍♂️&lt;/p&gt;

</description>
      <category>svg</category>
      <category>design</category>
      <category>vectorgraphics</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Sticky navigation bar without JavaScript</title>
      <dc:creator>Christoph Grabo</dc:creator>
      <pubDate>Wed, 14 Oct 2020 22:48:25 +0000</pubDate>
      <link>https://dev.to/asaaki/sticky-navigation-bar-without-javascript-4d1p</link>
      <guid>https://dev.to/asaaki/sticky-navigation-bar-without-javascript-4d1p</guid>
      <description>&lt;p&gt;Have you ever wanted to create a sticky menu, but without the hassle of writing extra JavaScript for it? CSS got you covered now!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftciln4pch883qdzbsjw1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftciln4pch883qdzbsjw1.gif" alt="Example animation of a sticky menu bar" width="720" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The key for success is the property &lt;code&gt;position: sticky&lt;/code&gt;. And despite it being marked as "partial support" for the majority browsers, it is working pretty well. The "partial" is due to the fact that some browser don't deal with that property correctly in some table setups. If you do not use tables and don't want some stickiness there, you're good to go.&lt;/p&gt;

&lt;p&gt;The following is my code change on my &lt;a href="https://markentier.tech/" rel="noopener noreferrer"&gt;personal site&lt;/a&gt;:&lt;/p&gt;

&lt;h2&gt;
  
  
  Before
&lt;/h2&gt;

&lt;h3&gt;
  
  
  JavaScript
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;navbar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.navbar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sticky&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;navbar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetTop&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;navbarScroll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageYOffset&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;sticky&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;navbar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sticky&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;navbar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sticky&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onscroll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;navbarScroll&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Stylesheet
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.navbar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.sticky&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  After
&lt;/h2&gt;

&lt;h3&gt;
  
  
  JavaScript
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// nope&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Stylesheet
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.navbar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sticky&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* it does not reposition right away,
             but determines at which point it sticks */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As the comment states, you will need &lt;code&gt;top&lt;/code&gt; to tell where to stick once the element scrolled to it. Meaning: as long as you have not scrolled the bar up to top zero of your current view, it will keep scrolling up. Afterwards it stays there.&lt;/p&gt;

&lt;p&gt;Not tested, but this could also work in all other directions. Someone up for a bottom bar? ;-)&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I think 2 lines of CSS are better than several in CSS and JS.&lt;/p&gt;

&lt;p&gt;What's not present yet is a way to apply different style when stuck or not, since there is no CSS selector for that. Dunno if that will ever come, but one can hope.&lt;/p&gt;

&lt;p&gt;I recently blogged about this and JS usage in general in my article &lt;a href="https://markentier.tech/posts/2020/10/wrote-javascript-to-avoid-javascript/" rel="noopener noreferrer"&gt;»How I wrote JavaScript to avoid JavaScript«&lt;/a&gt; - don't worry, you do not have to throw away everything like I did. But I want to give you some food for thought. Maybe you realize you solved your problems with way too much JS.&lt;/p&gt;

&lt;p&gt;Also check out the specs and development of HTML and CSS standards, there are always interesting things to discover. One of the biggest changes in recent years were flexboxes and grids, I guess many web designers welcomed these additions.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>css</category>
      <category>frontend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Rust 2021</title>
      <dc:creator>Christoph Grabo</dc:creator>
      <pubDate>Thu, 08 Oct 2020 16:33:33 +0000</pubDate>
      <link>https://dev.to/asaaki/rust-2021-4cn</link>
      <guid>https://dev.to/asaaki/rust-2021-4cn</guid>
      <description>&lt;p&gt;Over at my home blog I wrote about &lt;a href="https://markentier.tech/posts/2020/10/rust-2021/" rel="noopener noreferrer"&gt;what I want and wish from Rust&lt;/a&gt; in the next year and beyond.&lt;/p&gt;

&lt;p&gt;Also note that I will not republish full articles on dev.to anymore.&lt;/p&gt;

&lt;p&gt;So, please bookmark and visit &lt;a href="https://markentier.tech/" rel="noopener noreferrer"&gt;markentier.tech&lt;/a&gt; instead (and it even has Atom/RSS feeds if you like).&lt;/p&gt;

</description>
      <category>rust</category>
      <category>roadmap</category>
      <category>future</category>
      <category>development</category>
    </item>
    <item>
      <title>Make git faster in WSL2</title>
      <dc:creator>Christoph Grabo</dc:creator>
      <pubDate>Thu, 08 Oct 2020 15:18:30 +0000</pubDate>
      <link>https://dev.to/asaaki/make-git-faster-in-wsl2-156a</link>
      <guid>https://dev.to/asaaki/make-git-faster-in-wsl2-156a</guid>
      <description>&lt;p&gt;For quite some time I wondered why my prompt was so slow in my Linux environments under WSL2. After some digging I finally figured it out: not only has my prompt tool an issue, but also in general the filesystem performance of WSL2 is not great.&lt;/p&gt;

&lt;p&gt;On my blog I tell you the full story and provide you a solution on &lt;a href="https://markentier.tech/posts/2020/10/faster-git-under-wsl2/" rel="noopener noreferrer"&gt;how to speed up git for you within WSL2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I came not up with the fix, so I'm very grateful for the work of others. Writing it down and provide some explanations is my way of contributing back to the community.&lt;/p&gt;

</description>
      <category>wsl2</category>
      <category>git</category>
      <category>performance</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Nokogiri installation errors on macos</title>
      <dc:creator>Christoph Grabo</dc:creator>
      <pubDate>Sun, 30 Dec 2018 00:00:00 +0000</pubDate>
      <link>https://dev.to/asaaki/nokogiri-installation-errors-on-macos-3h8b</link>
      <guid>https://dev.to/asaaki/nokogiri-installation-errors-on-macos-3h8b</guid>
      <description>&lt;p&gt;Quick answer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bundle config build.nokogiri &lt;span class="nt"&gt;--use-system-libraries&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; bundle &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;This was originally posted &lt;a href="https://markentier.tech/posts/2018/12/ruby-bundler-nokogiri-macos/" rel="noopener noreferrer"&gt;on my blog&lt;/a&gt; here.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Every other year or so I want (or need) to install dependencies for a Ruby application on my Macbook, directly on the host instead of a VM or container. Mostly it's a Rails app.&lt;/p&gt;

&lt;p&gt;And every time our most "loved" dependency bails on me: &lt;a href="https://www.nokogiri.org/" rel="noopener noreferrer"&gt;&lt;code&gt;nokogiri&lt;/code&gt;&lt;/a&gt;. I think it always fails on a Mac. (At least once.)&lt;/p&gt;

&lt;p&gt;Because I never go directly to the documentation of whatever refuses to work, I usually google my way to a solution.&lt;br&gt;
In my case this was then harder than it should have been, so I write this down here for me as a reminder.&lt;/p&gt;

&lt;p&gt;The next time I google it, I hope to find my own blog post and will make the same expression at the end. Again.&lt;/p&gt;
&lt;h2&gt;
  
  
  How does it fail
&lt;/h2&gt;

&lt;p&gt;Try to get and build the dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# maybe a fancy Rails application&lt;/span&gt;
bundle &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And after a while …&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# snippet&lt;/span&gt;
An error occurred &lt;span class="k"&gt;while &lt;/span&gt;installing nokogiri &lt;span class="o"&gt;(&lt;/span&gt;1.8.5&lt;span class="o"&gt;)&lt;/span&gt;, and Bundler cannot &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Make sure that &lt;span class="sb"&gt;`&lt;/span&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;nokogiri &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s1"&gt;'1.8.5'&lt;/span&gt; &lt;span class="nt"&gt;--source&lt;/span&gt; &lt;span class="s1"&gt;'https://rubygems.org/'&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; succeeds before bundling.

In Gemfile:
  rails was resolved to 5.2.1.1, which depends on
    actioncable was resolved to 5.2.1.1, which depends on
      actionpack was resolved to 5.2.1.1, which depends on
        actionview was resolved to 5.2.1.1, which depends on
          rails-dom-testing was resolved to 2.0.3, which depends on
            nokogiri
&lt;span class="c"&gt;# /snippet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if you run what is suggested …&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;nokogiri &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s1"&gt;'1.8.5'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Building native extensions. This could take a while...
ERROR:  Error installing nokogiri:
  ERROR: Failed to build gem native extension.

# ... snip ...

Undefined symbols for architecture x86_64:
  "_iconv", referenced from:
      _main in conftest-451598.o
  "_iconv_open", referenced from:
      _main in conftest-451598.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
checked program was:
/* begin */
 1: #include "ruby.h"
 2:
 3: #include &amp;lt;stdlib.h&amp;gt;
 4: #include &amp;lt;iconv.h&amp;gt;
 5:
 6: int main(void)
 7: {
 8:     iconv_t cd = iconv_open("", "");
 9:     iconv(cd, NULL, NULL, NULL, NULL);
10:     return EXIT_SUCCESS;
11: }
/* end */
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also check the logs for later reference, too.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/Users/chris/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/extensions/x86_64-darwin-17/2.5.0-static/nokogiri-1.8.5/gem_make.out
/Users/chris/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/extensions/x86_64-darwin-17/2.5.0-static/nokogiri-1.8.5/mkmf.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This is a problem that it cannot work with the &lt;strong&gt;iconv&lt;/strong&gt; library currently present for linking.&lt;/p&gt;

&lt;p&gt;Alternatively also another library can cause some troubles: &lt;code&gt;libxml2&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then the output might look like …&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running 'compile' for libxml2 2.9.8... ERROR, review '/Users/chris/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/nokogiri-1.8.5/ext/nokogiri/tmp/x86_64-apple-darwin17.7.0/ports/libxml2/2.9.8/compile.log' to see what happened. Last lines are:
========================================================================
      _parseAndPrintFile in xmllint.o
  "_xmlXPathEval", referenced from:
      _doXPathQuery in xmllint.o
  "_xmlXPathFreeContext", referenced from:
      _doXPathQuery in xmllint.o
  "_xmlXPathFreeObject", referenced from:
      _doXPathQuery in xmllint.o
  "_xmlXPathIsInf", referenced from:
      _doXPathDump in xmllint.o
  "_xmlXPathIsNaN", referenced from:
      _doXPathDump in xmllint.o
  "_xmlXPathNewContext", referenced from:
      _doXPathQuery in xmllint.o
  "_xmlXPathOrderDocElems", referenced from:
      _parseAndPrintFile in xmllint.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to fix it
&lt;/h2&gt;

&lt;p&gt;Well, if one would pay attention to the output, sometimes the output already tells you what could be done.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;For the libxml case:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IMPORTANT NOTICE:

Building Nokogiri with a packaged version of libxml2-2.9.8
with the following patches applied:
    - 0001-Revert-Do-not-URI-escape-in-server-side-includes.patch
    - 0002-Fix-nullptr-deref-with-XPath-logic-ops.patch
    - 0003-Fix-infinite-loop-in-LZMA-decompression.patch

Team Nokogiri will keep on doing their best to provide security
updates in a timely manner, but if this is a concern for you and want
to use the system library instead; abort this installation process and
reinstall nokogiri as follows:

    gem install nokogiri -- --use-system-libraries
        [--with-xml2-config=/path/to/xml2-config]
        [--with-xslt-config=/path/to/xslt-config]

If you are using Bundler, tell it to use the option:

    bundle config build.nokogiri --use-system-libraries
    bundle install

Note, however, that nokogiri is not fully compatible with arbitrary
versions of libxml2 provided by OS/package vendors.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, the last commands are the things you should consider for a bundler based project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bundle config build.nokogiri &lt;span class="nt"&gt;--use-system-libraries&lt;/span&gt;
bundle &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can check the config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bundle config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Settings are listed in order of priority. The top value will be used.
gem.test
Set for the current user (/Users/chris/.bundle/config): "rspec"

gem.mit
Set for the current user (/Users/chris/.bundle/config): true

gem.coc
Set for the current user (/Users/chris/.bundle/config): true

build.nokogiri
Set for the current user (/Users/chris/.bundle/config): "--use-system-libraries"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file &lt;code&gt;~/.bundle/config&lt;/code&gt; for completion (shown as diff):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  ---
  BUNDLE_GEM__TEST: "rspec"
  BUNDLE_GEM__MIT: "true"
  BUNDLE_GEM__COC: "true"
&lt;span class="gi"&gt;+ BUNDLE_BUILD__NOKOGIRI: "--use-system-libraries"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it. Fixed!&lt;/p&gt;

&lt;h2&gt;
  
  
  Nokogiri documentation
&lt;/h2&gt;

&lt;p&gt;If I had consulted &lt;a href="https://www.nokogiri.org/tutorials/installing_nokogiri.html#install-with-system-libraries" rel="noopener noreferrer"&gt;the nokogiri documentation&lt;/a&gt; at all, I also would have got the hint earlier:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nokogiri will refuse to build against certain versions of libxml2, libxslt supplied with your operating system, and certain versions will cause mysterious problems. The compile scripts will warn you if you try to do this.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It focuses on libxml2, but the steps also help with the &lt;strong&gt;libiconv&lt;/strong&gt; issue, too.&lt;/p&gt;

&lt;p&gt;🤦&lt;/p&gt;

</description>
      <category>nokogiri</category>
      <category>ruby</category>
      <category>bundler</category>
      <category>rails</category>
    </item>
    <item>
      <title>Minimalism, Focus, Clean</title>
      <dc:creator>Christoph Grabo</dc:creator>
      <pubDate>Tue, 01 May 2018 00:00:00 +0000</pubDate>
      <link>https://dev.to/asaaki/minimalism-focus-clean-21b1</link>
      <guid>https://dev.to/asaaki/minimalism-focus-clean-21b1</guid>
      <description>&lt;p&gt;&lt;em&gt;Start with whatever you have, but don't stop reaching for something better. Why I love minimalistic and simplistic web designs.&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  One: Nothing is forever
&lt;/h3&gt;

&lt;p&gt;A few days ago I pointed a colleague to my freshly hatched blog, because I wanted to show him my recent post. In the end we didn't even chat about the post, but web design and the job (he's a web designer), challenges and opportunities and interesting stuff to do and creativity and the past and the kind reminder that I'm not the youngest anymore. What kept nagging in the back was the comment about my (now past) design.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F05%2Fminimalism-focus-clean-redesign%2Fmtt-old.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F05%2Fminimalism-focus-clean-redesign%2Fmtt-old.png" alt="markentier.tech in old design"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Oh man you need need need to update your site&lt;/p&gt;

&lt;p&gt;Want some design help on that bitch?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As much as I appreciate the help and support, but especially for my personal project I tend to do as much as possible on my own. Also taste is highly subjective; I live in Berlin, Germany, I see stuff, I know what I'm talking about. And I do not need to agree with the style and display of others and others' work. I nod (or shrug), I go, and probably forget about it ten minutes later anyway.&lt;/p&gt;

&lt;p&gt;But there was a point to the above message. Something he couldn't even know. The design was a temporary solution, and I dug it up from a 4 years old proof of concept work I've done. It was the quickest way to get my site up and running without looking too shabby. I didn't need to seek for a pre-made design somewhere else and I didn't need to spend too much time on creating one from scratch.&lt;/p&gt;

&lt;p&gt;While an old design doesn't need to be bad at all, mine wasn't really finished and fully thought through though. And so this sparked the urge to do something about it.&lt;/p&gt;

&lt;p&gt;I also knew that I wanted to change the logo independently from a redesign, but now all things came together.&lt;/p&gt;

&lt;p&gt;&lt;a href="/m.svg"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fm.svg" alt="m (reference logo file; SVG)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Two: Anything is possible
&lt;/h3&gt;

&lt;p&gt;I love minimalism, I really do. And I wish I could follow this principle a bit more in my daily life, for now the digital one needs to be sufficient enough for this.&lt;/p&gt;

&lt;p&gt;Not all my previous designs and themes were minimal, simple, or even easy to use. Some were artistic; I think in the early 2000s artsy, graphical and very detailed themes were a thing. Sadly I forgot to bookmark (and screenshot) some of my favourites, I cannot even remember the names of the people, so no hint how to find them again. Well, it's history anyway.&lt;/p&gt;

&lt;p&gt;In the end I always come to enjoy a design and layout which is not overloaded with stuff and cruft. Sometimes I will add something here and there. And just a bit later I think I should remove something again.&lt;/p&gt;

&lt;p&gt;Also: minimalism doesn't necessarily mean total emptyness or absense of everthing. But I came to love the huge blanks, and the vast endlessness of the plains.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- basic SVG logo markup for markentier.tech --&amp;gt;
&amp;lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"&amp;gt;
  &amp;lt;path fill="none"stroke="#0c1328"
        stroke-linecap="round"
        stroke-linejoin="round"
        stroke-width="64"
        d="M480 506v208-128s0-72 64-72 64 72 64 {…}"/&amp;gt;
&amp;lt;/svg&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Three: Something is changing
&lt;/h3&gt;

&lt;p&gt;I love vector graphics. And so I love SVG. I remember to have always liked this image format since discovery. It was quite sad, that it also took quite some time to have proper cross browser support. If you ignore old IEs, then the present day situation is awesome. SVG v2 is not there yet (and I really want to get rid of the &lt;code&gt;xlink&lt;/code&gt; namespace), but the currently implemented spec is already good for a lot of use cases.&lt;/p&gt;

&lt;p&gt;One perfect example are logos and icons. As you already see, my website's logo is delivered and rendered as SVG, I couldn't get it smaller than that anyway, any PNG, GIF or JPEG would always be much, much bigger than the following path definition:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;M480 506v208-128s0-72 64-72 64 72 64 72v128-128s0-72 64-72 64 72 64 72v128-128s0-72 64-72 64 72 64 72 1 85 1 128c-1 28-24 54-24 54-126 161-348 207-527 111-179-97-262-309-196-501 66-193 261-309 462-275 200 33 347 206 348 409&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yes, exactly, this is all. It is probably the most compact version I could use. Hence the usage of a 1024x1024 viewBox and big numbers, just to avoid decimals, which carry the extra dot. This is a pro-tipp btw, always design your stuff on a huge canvas 1000 pixels or bigger in dimensions. Then with tools like SVGOMG (or already in your vector graphics program) you can save the file with zero decimals (so only integers) without loosing significant information.&lt;/p&gt;

&lt;p&gt;If you want to get more excited about SVGs:&lt;/p&gt;

&lt;p&gt;&lt;a rel="external noopener noreferrer" href="https://www.youtube.com/watch?v=lMFfTRiipOQ"&gt;&lt;/a&gt;&lt;a href="https://www.youtube.com/watch?v=lMFfTRiipOQ" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=lMFfTRiipOQ&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a rel="external noopener noreferrer" href="https://www.youtube.com/watch?v=ADXX4fmWHbo"&gt;&lt;/a&gt;&lt;a href="https://www.youtube.com/watch?v=ADXX4fmWHbo" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=ADXX4fmWHbo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are definitely more, but above videos should be a nice starting point. I might come back to this topic in the future.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F05%2Fminimalism-focus-clean-redesign%2Fmtt-now.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F05%2Fminimalism-focus-clean-redesign%2Fmtt-now.png" alt="markentier.tech new design"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Four: Everything is evolving
&lt;/h3&gt;

&lt;p&gt;The screenshot above might look a bit weird (when you read this article on &lt;a href="https://markentier.tech/" rel="noopener noreferrer"&gt;markentier.tech&lt;/a&gt;), because it fits perfectly into the current design.&lt;/p&gt;

&lt;p&gt;I think this represents an interpretation of minimalism. At least one I like. There didn't went too much process or sciene into it, at least not consciously. Of course, I'm aware of some color basics, contrasts and a basic understanding of accessibility. It won't be perfect yet. And probably I won't bake in some changes you'd like to see.&lt;/p&gt;

&lt;p&gt;But I do believe that my articles should be consumable without too much distraction or fatigue or strain. Since it is heavily color range reduced, color blind people (or with partial deficiency) should be able to read and navigate the site without loosing anything or getting confused. I haven't tested screenreaders, but I hope I don't use design magic which would impact your experience here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F05%2Fminimalism-focus-clean-redesign%2Fmtt-now-scrolled.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F05%2Fminimalism-focus-clean-redesign%2Fmtt-now-scrolled.png" alt="markentier.tech new design, page scrolled down (shows tiny logo and sticky navbar)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also still work on the performance aspect of it. And I also push for more modern browsers. Since it is my personal blog, I think I can afford to "loose" some customers, or at least give them not the full pleasure by not being always fully backwards compatible.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you have questions or just want to know in more detail how something works here, ping me on &lt;a href="https://twitter.com/asaaki" rel="noopener noreferrer"&gt;twitter&lt;/a&gt; or open an issue at the &lt;a href="https://github.com/markentier/markentier.tech/issues" rel="noopener noreferrer"&gt;repo&lt;/a&gt; for this site, I'm happy to explain, glad if you can learn something, and I welcome feedback and corrections.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>minimalism</category>
      <category>design</category>
      <category>wedesign</category>
      <category>ux</category>
    </item>
    <item>
      <title>TIL: rebeccapurple</title>
      <dc:creator>Christoph Grabo</dc:creator>
      <pubDate>Wed, 11 Apr 2018 00:00:00 +0000</pubDate>
      <link>https://dev.to/asaaki/til-rebeccapurple-3c6f</link>
      <guid>https://dev.to/asaaki/til-rebeccapurple-3c6f</guid>
      <description>&lt;p&gt;While polishing my theme, I discovered Rebecca. I love it, when technology gets some human touch.&lt;/p&gt;

&lt;p&gt;I was just reading about the color data type in CSS &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/color_value" rel="noopener noreferrer"&gt;over at MDN&lt;/a&gt; when I saw that a new named color will be part of the &lt;a href="https://drafts.csswg.org/css-color/" rel="noopener noreferrer"&gt;CSS Color Module Level 4&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There was a link to &lt;a href="https://codepen.io/trezy/post/honoring-a-great-man" rel="noopener noreferrer"&gt;this Codepen blog post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Rebecca was Eric Meyer's daughter. She was diagnosed with cancer. &lt;a href="https://meyerweb.com/eric/thoughts/2014/06/09/in-memoriam-2/" rel="noopener noreferrer"&gt;On her sixth birthday she left us.&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This past Friday, June 19th, 2014, the community to which Eric has given so very much &lt;a href="https://lists.w3.org/Archives/Public/www-style/2014Jun/0312.html" rel="noopener noreferrer"&gt;has seen fit to name #663399 as rebeccapurple&lt;/a&gt; with his permission.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Eric Meyer is probably most well-known for &lt;a href="https://meyerweb.com/eric/tools/css/reset/" rel="noopener noreferrer"&gt;reset.css&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;More TIL: Btw the changes for CSS 3 to 4 are also interesting from a technical perspective: &lt;a href="https://drafts.csswg.org/css-color/#changes" rel="noopener noreferrer"&gt;https://drafts.csswg.org/css-color/#changes&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>css</category>
      <category>color</category>
      <category>human</category>
      <category>honor</category>
    </item>
    <item>
      <title>Hitchhiker's Guide To Structured Data</title>
      <dc:creator>Christoph Grabo</dc:creator>
      <pubDate>Tue, 03 Apr 2018 00:00:00 +0000</pubDate>
      <link>https://dev.to/asaaki/hitchhikers-guide-to-structured-data-2lcc</link>
      <guid>https://dev.to/asaaki/hitchhikers-guide-to-structured-data-2lcc</guid>
      <description>&lt;p&gt;A tiny journey to the building blocks of the semantic web and how they make our lives much easier.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“There is a theory which states that if ever anyone discovers exactly what the Universe is for and why it is here, it will instantly disappear and be replaced by something even more bizarre and inexplicable. There is another theory which states that this has already happened.”&lt;/em&gt;&lt;br&gt;&lt;br&gt;
—Hitchhiker's Guide To The Galaxy&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F04%2Fhitchhikers-guide-to-structured-data%2Fcode-snippet.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F04%2Fhitchhikers-guide-to-structured-data%2Fcode-snippet.png" alt="Example of Structured Data for HTML; Person and Place: 'Douglas Adams was born in Cambridge'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I started using computers, I never thought about if and how they could help me to do things — and if that in turn would improve my life. I automatically assumed that we invented tools, machines, devices, phones, computers, and so on because we wanted to make our lives better, and to think less about tedious tasks by making the complex and especially complicated ones easier and more usable.&lt;/p&gt;

&lt;p&gt;In the late 90s I not only learned but experienced that computers are connected. There was this thing called internet, mostly advertised in the form of WWW, the world wide web: gazillions of documents more or less linked to each other. Mostly plastered with flashy animations saying that this place is under construction. And the &lt;code&gt;&amp;amp;lt;marquee&amp;amp;gt;&lt;/code&gt; tag made us feel as important as breaking news or a stock market ticker.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F04%2Fhitchhikers-guide-to-structured-data%2Fpammiejay.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F04%2Fhitchhikers-guide-to-structured-data%2Fpammiejay.png" alt="Pammie Jay's Webpage (geocities)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ah, yes, the good old internet. In the beginning quite static and mostly text, but it became richer, more versatile, dynamic, and included images, audio, and video. Besides the static sites, we built web apps, APIs and made the internet a place to interact with each other and with machines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nowadays our fridge "Marvin" can tell us via Twitter that he feels quite empty and sad
&lt;/h3&gt;

&lt;p&gt;As a kid I read tons of books, mostly fiction, of course, but I remember I visited our library in the village quite often and borrowed books explaining the world to me. But I consumed so much, I couldn't keep all the information in my head, nowadays I'm not significantly more knowledgeable than decades ago. I remember bits and pieces, but more importantly, I usually remember where I need to look for the actual data to fill in the gaps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F04%2Fhitchhikers-guide-to-structured-data%2Fgoogle-1997.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F04%2Fhitchhikers-guide-to-structured-data%2Fgoogle-1997.png" alt="Google in 1997"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I grew up with Google from the beginning. And while the old and manually maintained catalogs and directories were a good starting point to explore the digital universe, search engines made my life easier. Now I didn't even need to keep an index and graph of information fragments in my head anymore, I could shift to the skill of asking the right questions.&lt;/p&gt;

&lt;p&gt;But there are a few problems with most general purpose search engines. The easiest way to build an index is to crawl the internet and grab the full text of every page discovered and connect it to keywords. Later when somebody searches for this keyword they will get a huge list of results. The algorithms became smarter, the better you can formulate your query, the more relevant and condensed the list will be.&lt;/p&gt;

&lt;p&gt;But if you think about it, you can already ask very specific questions, and most search engines wouldn't really be able "to get it" right from the beginning.&lt;/p&gt;

&lt;h3&gt;
  
  
  "What are the books the author Douglas Adams has written?"
&lt;/h3&gt;

&lt;p&gt;If you would have tried this maybe 10 or even just 5 years ago and you got lucky, then some result on page one would have pointed you to an article about his publications. If you're not using Google or WolframAlpha the situation is still quite disappointing.&lt;/p&gt;

&lt;p&gt;Today I'm quite impressed how this works out:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F04%2Fhitchhikers-guide-to-structured-data%2Fdouglas-adams-books.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F04%2Fhitchhikers-guide-to-structured-data%2Fdouglas-adams-books.jpg" alt="Google.com search results for 'What are the books the author Douglas Adams has written?'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can also see, the first organic result is about the author on Wikipedia, only the second one is close to what I was asking. But the top bar with the book covers is rocking it! &lt;em&gt;(I first tried with Google.de and there it was just the 3rd result.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And I will make a bold statement here: giving you such specific and contextually relevant information is not possible solely based on some full text search index; I believe this was driven by structured data (and potentially stored in a graph database … maybe).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;My internal QuickCheck program narrowed it down to &lt;strong&gt;"douglas adams books"&lt;/strong&gt; , but this is not the point (although parsing the input of a search box is probably a nice adventure on its own).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We can further decompose our query and figure that it consists of a tuple: &lt;strong&gt;"douglas adams"&lt;/strong&gt; and &lt;strong&gt;"books"&lt;/strong&gt;. The first one describes the name of a person, the second one describes an entity type, and obviously we want to find a connection between this very person and the books he wrote. Since people rarely mentioned which books they have read, the query parser can already make an educated guess here and put as a relationship type a &lt;strong&gt;"has written"&lt;/strong&gt; (also "has published" could work).&lt;/p&gt;

&lt;p&gt;Or expressed in a pseudo query language and very simplistic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;books&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"Douglas Adams"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, first you would need to resolve an actual author/person entity from your database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;people&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"Douglas Adams"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then you realize that your people table/database could have multiple entries, and you would try to filter based on occupation or hobby. Maybe you have them ranked by popularity and will just take the highest ranked entry. Another option is to resolve the full query for each person and check which one has written the most books (if any at all) and use this one.&lt;/p&gt;

&lt;p&gt;Whatever the approach is, you need to have your data stored in a structured way. And if you want to build a search engine, a knowledge database, or any kind of application querying and presenting such information (but not storing all the information itself) you need to rely on other sources providing them in a machine consumable way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F04%2Fhitchhikers-guide-to-structured-data%2Fsemantic-web-wordle.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F04%2Fhitchhikers-guide-to-structured-data%2Fsemantic-web-wordle.png" alt="Wordle cloud for 'Semantic Web'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The idea of structured data and a semantic web is not new. And if you have used any blogging software then you also used some aspects of structuring: categories and tags.&lt;/p&gt;

&lt;p&gt;Depending on your needs you even introduced your own taxonomies to describe and differentiate your articles. As an example: you're a movie reviewer and classify your reviews based on mood, target audience, and genres; all of these dimensions help you to describe your data, make it more discoverable, support consuming systems, and directly or indirectly give your readers more context and meta data, usually in a quicker and easier way, so they can decide if they want to dive deeper into your blog or look somewhere else.&lt;/p&gt;

&lt;p&gt;The article previews/cards on Facebook and Twitter are also driven by structured data. When you look into the source code of some HTML pages you can spot some interesting meta tags with names or properties like &lt;code&gt;"og:title"&lt;/code&gt;, &lt;code&gt;"twitter:creator"&lt;/code&gt;, or &lt;code&gt;"article:author"&lt;/code&gt;. They are used to fill the pieces of the preview. And to be honest — you will most likely click on a blog post when it has a nice cover image and description line won't you?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F04%2Fhitchhikers-guide-to-structured-data%2Ftwitter-cards.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F04%2Fhitchhikers-guide-to-structured-data%2Ftwitter-cards.png" alt="Twitter Cards"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It took us years to bring in more data, or to take advantage of the full potential of structured data, but nowadays this concept is the foundation of the web. Even with AI on the rise machines can do their job best if they can process the data in a structured way.&lt;/p&gt;

&lt;p&gt;I think the art is to display content in a pleasant, human-readable manner and unobtrusively enrich it with machine consumable semantic information. This so that we can build interfaces and applications that can make sense of all of the data, and then visualize it in a different and more approachable way.&lt;/p&gt;

&lt;p&gt;If you want to see what structured data can look like, then head over to wikidata:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F04%2Fhitchhikers-guide-to-structured-data%2Fwikidata-q42-douglas-adams.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F04%2Fhitchhikers-guide-to-structured-data%2Fwikidata-q42-douglas-adams.png" alt="Wikidata.org page for Q42: Douglas Adams"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These entity pages are very formal and not the most beautiful ones to read about a person's life, but they contain very valuable information by describing what this entity is about and what kind of relationship it has to other entities.&lt;/p&gt;

&lt;p&gt;Such data is designed to be consumed by other services. For example the infoboxes on the right side of Wikipedia articles are mostly supplemented with wikidata information.&lt;/p&gt;

&lt;p&gt;Some people have built tools to nicely visualize such data:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F04%2Fhitchhikers-guide-to-structured-data%2Fwikidata-graph-q42-p800.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F04%2Fhitchhikers-guide-to-structured-data%2Fwikidata-graph-q42-p800.png" alt="wikidata graph builder: Q42, P800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How could you use the structured data from wikidata for your own project?
&lt;/h3&gt;

&lt;p&gt;You have built a website for your international book club. It comes with a knowledge base, which supports already 42 languages. While managing the content for the authors and books is pretty easy with Contentful, you realize that localization is sometimes painful, because you have a hard time to figure out what the translations of each person's name are.&lt;/p&gt;

&lt;h4&gt;
  
  
  Don't panic!
&lt;/h4&gt;

&lt;p&gt;A friend of yours comes up with the idea to create a UI extension for Contentful, where you can look up the wikidata and automatically fill the translation fields for the names and other common properties. She has even developed a first prototype for you:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/asaaki/cf-wikidata-ui-extension-example" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarkentier.tech%2Fposts%2F2018%2F04%2Fhitchhikers-guide-to-structured-data%2Fexample-Q42.gif" alt="Contentful UI extension example for wikidata.org integration"&gt;&lt;/a&gt;&lt;small&gt;&lt;a href="https://github.com/asaaki/cf-wikidata-ui-extension-example" rel="noopener noreferrer"&gt;github.com/asaaki/cf-wikidata-ui-extension-example&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;You really like it and start to think about how to automate a lot of other steps. For example the extension could fetch all the known books and create drafts for each of them and link them back to the author entry. Since wikidata also provides links to other Wikimedia services you could store them with your knowledge base articles as well. You integrate a lot of links to other sites and maybe want to create some kind of preview box similar to Twitter Cards.&lt;/p&gt;

&lt;p&gt;With well maintained structured data the possibilities are unlimited. So, go out, explore, and build awesome stuff.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Curiously enough, the only thing that went through the mind of the bowl of petunias as it fell was Oh no, not again. Many people have speculated that if we knew exactly why the bowl of petunias had thought that we would know a lot more about the nature of the Universe than we do now.”&lt;/em&gt;&lt;br&gt;&lt;br&gt;
—Hitchhiker's Guide To The Galaxy&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;So Long, and Thanks for All the Fish!&lt;/strong&gt;&lt;/em&gt; 🐟&lt;/p&gt;

</description>
      <category>structured</category>
      <category>data</category>
      <category>semanticweb</category>
      <category>contentful</category>
    </item>
    <item>
      <title>Progressive Web App</title>
      <dc:creator>Christoph Grabo</dc:creator>
      <pubDate>Sun, 01 Apr 2018 00:00:00 +0000</pubDate>
      <link>https://dev.to/asaaki/progressive-web-app-nfi</link>
      <guid>https://dev.to/asaaki/progressive-web-app-nfi</guid>
      <description>&lt;p&gt;&lt;a href="https://markentier.tech/" rel="noopener noreferrer"&gt;markentier.tech&lt;/a&gt; is a Progressive Web App now, too. What does it mean and why are PWAs so cool? Main reason: it is about speed!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Progressive Web Apps (PWAs) are web applications that are regular web pages or websites, but can appear to the user like traditional applications or native mobile applications. The application type attempts to combine features offered by most modern browsers with the benefits of a mobile experience.&lt;br&gt;
—&lt;a href="https://en.wikipedia.org/wiki/Progressive_Web_Apps" rel="noopener noreferrer"&gt;Wikipedia: Progressive Web Apps&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I haven't really followed the development of &lt;strong&gt;Progressive Web Applications&lt;/strong&gt; (short: &lt;code&gt;PWA&lt;/code&gt;) for quite some time. And I think the last time I experimented with anything around this topic was, when &lt;em&gt;Application Cache&lt;/em&gt; (or &lt;a href="https://www.html5rocks.com/en/tutorials/appcache/beginner/" rel="noopener noreferrer"&gt;AppCache&lt;/a&gt;) was still a thing. So yeah, long time ago.&lt;/p&gt;

&lt;p&gt;I have forgotten about web site/app optimization, simply because I had no real web site to build and maintain for years. The sites I had were either abandoned or I didn't care so much about the performance of them.&lt;/p&gt;

&lt;p&gt;The very first question a lot of people might ask is:&lt;/p&gt;

&lt;h3&gt;
  
  
  Why even bother to read about it, if all I'll make will be a static site anyway?
&lt;/h3&gt;

&lt;p&gt;Well, if you do care about web performance and speed, you might want to consider all the tools available to you.&lt;/p&gt;

&lt;p&gt;If you in full control of the output (HTML, CSS, JS, images, fonts, ...) you have already lots of opportunities. You can minimize and compress the files. The HTML, CSS, and JavaScript stuff is fairly easy, for all of them exist online services or CLI executables.&lt;/p&gt;

&lt;p&gt;But do not forget your assets like images (and audio/video if you host them yourself), most of them can be made smaller. PNG and JPEG are still the most used image file formats (well, also GIFs for animations), &lt;a href="https://developers.google.com/speed/webp/" rel="noopener noreferrer"&gt;WebP&lt;/a&gt; is there, but I haven't really seen serious usage yet. Each format usually supports either lossless and/or lossy compressions. I still prefer PNG as my standard image format, and I like that there are tools out there helping me to squeeze out the last tiny bit to get to a pretty small file size.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools for minification and compression
&lt;/h3&gt;

&lt;p&gt;Before I come to the PWA part, let's have a look at what can (and should) be done, no matter what kind of website you have.&lt;/p&gt;

&lt;h4&gt;
  
  
  HTML
&lt;/h4&gt;

&lt;p&gt;So far I will only mention my go-to tool here: &lt;a href="http://www.html-tidy.org/" rel="noopener noreferrer"&gt;tidy-html5&lt;/a&gt;. I &lt;a href="https://dev.to/posts/2018/03/from-cobalt-to-gutenberg/#tidy-html5"&gt;elaborated a bit further in my last post&lt;/a&gt;, when I wrote about changing my static site generator.&lt;/p&gt;

&lt;p&gt;On the other hand the overhead of bloated and whitespace cluttered HTML pages is proably not the biggest issue when it comes to fat sites.&lt;/p&gt;

&lt;h4&gt;
  
  
  Stylesheets (CSS) and JavaScript (JS)
&lt;/h4&gt;

&lt;p&gt;Instead of recommending a specific tool I'd rather point to projects like &lt;a href="https://webpack.js.org/" rel="noopener noreferrer"&gt;webpack&lt;/a&gt;, &lt;a href="https://parceljs.org/" rel="noopener noreferrer"&gt;parcel&lt;/a&gt;, or your favourite static site generator of choice with proper support. Also if you use a service like &lt;a href="https://www.netlify.com/" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt;, that you can enable asset optimization and they will take care of the byte crunching for your, the same goes for the&lt;/p&gt;

&lt;p&gt;Even if you use WordPress I know that people have written nice plugins to help you with such task. Nobody needs to do this by hand anymore. The same goes for images as well, but for reasons I dedicate it its own section:&lt;/p&gt;

&lt;h4&gt;
  
  
  PNG and other images
&lt;/h4&gt;

&lt;p&gt;While services like Netlify claim to support compression of images, I'm pretty sure they will have trade-offs to publish and deliver your site faster. Since images are one of the biggest pieces of the total page weight, I think they deserve more attention beforehand.&lt;/p&gt;

&lt;p&gt;First of all do you know which size you need already? Do not simply put your desired image unaltered into your blog post. Unless you always choose the too small images, you probably end up embedding way too huge files instead. Nobody really needs that 3000x5000 photograph when reading your article. If you want to provide them that original file for later usage, give it a link to it for download. So resize the image to your needs. Consider all the usual presentation layers. Most people can live with a 1024 pixels maximum on either height or width, depending on your theme you might be able to use even smaller default sizes.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I use an aspect ratio of &lt;code&gt;2:1&lt;/code&gt;, so my default image dimension is &lt;code&gt;1024x512&lt;/code&gt;, which is used for my cover and further images. I use this maximum as default even though my article body width is a bit smaller. The reason is that the cover image is also used for &lt;a href="https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/abouts-cards" rel="noopener noreferrer"&gt;Twitter Cards&lt;/a&gt; (and Facebook) presentation. Both platforms either support or suggest bigger sizes, but for now I think this is overkill.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As said, I love PNGs, even though for photos the JPEG format might be better suited. Anyway there are a bunch of tools for PNG optimization.&lt;/p&gt;

&lt;p&gt;The most well-known is &lt;a href="http://www.imagemagick.org/" rel="noopener noreferrer"&gt;ImageMagick&lt;/a&gt;, which can be used for a lot of different image formats. I used it especially for resizing and generating my thumbnails of the cover images. Resizing down to the visibly rendered size saves already tons of bytes and therefore bandwidth!&lt;/p&gt;

&lt;p&gt;Speaking of thumbnails I go even further, because they cannot be only small in dimensions, but highly reduced and compressed, since they serve as a visual hook, but do not need to be the prettiest pictures ever.&lt;/p&gt;

&lt;p&gt;So something neat is the reduction of the color palette with &lt;a href="https://pngquant.org/" rel="noopener noreferrer"&gt;pngquant&lt;/a&gt;; people familiar with GIF optimization now that this helps already a lot.&lt;/p&gt;

&lt;p&gt;Second is done by &lt;a href="http://optipng.sourceforge.net/" rel="noopener noreferrer"&gt;optipng&lt;/a&gt;. &lt;em&gt;pngquant&lt;/em&gt; already can do a lot of heavy lifting, so maybe the companion doesn't so much afterwards, but maybe it will find a file for further optimization.&lt;/p&gt;

&lt;p&gt;Initially I also had &lt;a href="https://pmt.sourceforge.io/pngcrush/" rel="noopener noreferrer"&gt;pngcrush&lt;/a&gt; in the pipeline, but after checking the actual byte size of the files I saw that it mostly increased afterwards; I removed it, because I'm not interested in de-optimization of my content. 😅&lt;/p&gt;

&lt;p&gt;For GUI lovers there is a handy tool for macOS users: &lt;a href="https://imageoptim.com/mac" rel="noopener noreferrer"&gt;ImageOptim&lt;/a&gt;. This works on a bunch of image file formats and with quite some tools under the hood.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caching
&lt;/h3&gt;

&lt;p&gt;Minimization and compression are just the beginning of a fast served site. Once all the files are up on a server, waiting to be delivered all around the world, they also do not change that often. Especially your very static pages with all the assets are unlikely to change (like blog posts and about and imprint pages).&lt;/p&gt;

&lt;p&gt;So, to prevent a constant flow of all the bits and bytes for each request, we want to cache them, and ideally as close to the user as possible, so that the next time they request the exact same data, they are served from their devices instead. This becomes highly efficient for repetitively used and requested files like CSS and JS, which are shared across your whole site. Also your logo and header images do not need to travel each time that way.&lt;/p&gt;

&lt;p&gt;Intermediate steps are server-side caches and CDNs by the way, but I'm not focussing on this right now.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Also: Caching in itself is a tricky story and should probably deserve its own article.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Generally speaking the &lt;a href="https://developer.mozilla.org/de/docs/Web/HTTP/Headers/Cache-Control" rel="noopener noreferrer"&gt;Cache-Control header&lt;/a&gt; is a good starting point and helps browsers to determine, what and for how long it should try to keep a file in at hand instead of fetching it from the internet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1umrkc0yzotlrcus7phx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1umrkc0yzotlrcus7phx.png" alt="Google Lighthouse: Report" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Next level: Progressive Web
&lt;/h3&gt;

&lt;p&gt;Although I have heard of &lt;em&gt;Progressive Web Application&lt;/em&gt; already, I never paid much attention to it. I surely thought that it might be cool to look into this topic, once I figured out a web app project. A static site was definitely not a trigger. I could not have been more wrong.&lt;/p&gt;

&lt;p&gt;When I run the &lt;strong&gt;Audits&lt;/strong&gt; tool in the Google Chrome developer tools, I had no idea what this &lt;a href="https://developers.google.com/web/tools/lighthouse/" rel="noopener noreferrer"&gt;Lighthouse&lt;/a&gt; stuff is. I remember that in previous version of Chrome, there was this &lt;em&gt;PageSpeed Insights&lt;/em&gt; tab, which is deprecated and only available as a &lt;a href="https://developers.google.com/speed/pagespeed/insights/" rel="noopener noreferrer"&gt;web service&lt;/a&gt; nowadays. It's still a useful tool on its own. But &lt;strong&gt;Lighthouse&lt;/strong&gt; provides much more performance and optimization data.&lt;/p&gt;

&lt;p&gt;So when the report was generated I was a bit confused that it had this &lt;em&gt;Progressive Web App&lt;/em&gt; section. Sure, some sites might need this information, but the whole internet is not only about dynamic apps, is it?&lt;/p&gt;

&lt;p&gt;Well, it was about time to dive into this previously neglected topic again. I read and learned about the &lt;a href="https://developer.mozilla.org/de/docs/Web/Manifest" rel="noopener noreferrer"&gt;Web App Manifest (manifest.json)&lt;/a&gt; and the &lt;a href="https://developers.google.com/web/progressive-web-apps/checklist" rel="noopener noreferrer"&gt;PWA Checklist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One important step for the baseline was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All app URLs load while offline&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With the suggestion to fix it by doing this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use a Service Worker.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Oh, I also read about &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API" rel="noopener noreferrer"&gt;them&lt;/a&gt;, but for the same reason above I just ignored this topic, too. If you want to learn what you can do with them, check out &lt;a href="https://serviceworke.rs/" rel="noopener noreferrer"&gt;serviceworke.rs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a starter I went for the &lt;a href="https://github.com/GoogleChromeLabs/airhorn/blob/master/app/sw.js" rel="noopener noreferrer"&gt;pretty simple setup provided by Google&lt;/a&gt;. But also iterated further. Tell me if something doesn't work out well or the site just looks ugly (unstyled).&lt;/p&gt;

&lt;p&gt;The cool thing is, that it not only makes your site perform faster and cache items, but also provides offline functionality.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5h084y44v40pb3hjk73f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5h084y44v40pb3hjk73f.png" alt="markentier.tech PWA screenshots" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The screenshots above are an example of how it would look like, if you decided to add &lt;em&gt;markentier.tech&lt;/em&gt; to your homescreen. Coloring the address bar is also a nice gimmick.&lt;/p&gt;

&lt;p&gt;Until now I cannot tell you much more about PWAs yet, since I'm learning the stuff right away on the go.&lt;/p&gt;

&lt;p&gt;I encourage you to look into this topic as well. I hope I provided you with enough links to start your own journey.&lt;/p&gt;

</description>
      <category>performance</category>
      <category>optimization</category>
      <category>speed</category>
      <category>caching</category>
    </item>
    <item>
      <title>From cobalt.rs to gutenberg</title>
      <dc:creator>Christoph Grabo</dc:creator>
      <pubDate>Wed, 28 Mar 2018 00:00:00 +0000</pubDate>
      <link>https://dev.to/asaaki/from-cobaltrs-to-gutenberg-2kdm</link>
      <guid>https://dev.to/asaaki/from-cobaltrs-to-gutenberg-2kdm</guid>
      <description>&lt;p&gt;I wish I could have the features of both tools, but for now I will use gutenberg over cobalt. A tiny migration and feature comparison story.&lt;/p&gt;

&lt;p&gt;While &lt;a href="https://cobalt-org.github.io/" rel="noopener noreferrer"&gt;cobalt&lt;/a&gt; is a pretty nice and easy static site generator written in Rust (in the vein of &lt;a href="https://jekyllrb.com/" rel="noopener noreferrer"&gt;Jekyll&lt;/a&gt;, the famous tool used for GitHub pages), I struggled a bit. Especially that the stylesheet compilation is not enabled by default yet, but also other tiny annoyances or missing features played into that. I still want to stick with &lt;a href="https://www.rust-lang.org/" rel="noopener noreferrer"&gt;Rust&lt;/a&gt; and the only other active option here is &lt;a href="https://www.getgutenberg.io/" rel="noopener noreferrer"&gt;gutenberg&lt;/a&gt;. But to be honest, this one also comes with its complications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cobalt&lt;/strong&gt; is your tool for a quick blog-like setup, because it supports the usual two different article types of (static) pages and blog posts. &lt;strong&gt;Gutenberg&lt;/strong&gt; doesn't really have such distinction, and therefore you need to be more elaborate if you want to achieve the same.&lt;/p&gt;

&lt;h3&gt;
  
  
  Not all perfect, but we're getting there
&lt;/h3&gt;

&lt;p&gt;Since both projects are pretty young, they share similar shortcomings: the template libraries are usually not feature complete yet or behave a slightly bit different than their spiritual role models. Mostly one can work around those issues, but it's a bit painful still.&lt;/p&gt;

&lt;p&gt;Gutenberg shines here a bit brighter, since it supports macros and shortcodes. Especially the latter is something nice, because I can quickly enrich my pages with snippets, which are usually weird or hard to be done purely in Markdown. Embedded HTML is not the nicest thing and if I can abstract them away, I'm all in for that.&lt;/p&gt;

&lt;p&gt;Another tiny plus: Gutenberg is supported by &lt;a href="https://www.netlify.com/" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt;, therefore I do not need to install the binary somehow on their side. Now I can let it build the final site on deployment and skip committing a prebuilt project. Pretty sure I could have done it with cobalt as well, but this way it worked out of the box.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools, tools, tools
&lt;/h3&gt;

&lt;p&gt;Speaking of tooling: I was a bit adventurous and bundled two tiny binaries anyway and it worked.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;fd&lt;/code&gt; - the simpler find, written in Rust
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/sharkdp/fd" rel="noopener noreferrer"&gt;fd&lt;/a&gt; advertises itself as &lt;em&gt;A simple, fast and user-friendly alternative to 'find'.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And according to their benchmarks it is incredibly fast. But I also like the easier command-line options. The interface is designed for the common cases. And it is written in Rust. Have I mentioned that I love Rust? No? ;-)&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;tidy-html5&lt;/code&gt; - getting rid of awkward whitespaces and indentation
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;The granddaddy of HTML tools.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;The granddaddy of HTML tools.&lt;/em&gt; — well, yes, &lt;a href="http://www.html-tidy.org/" rel="noopener noreferrer"&gt;tidy&lt;/a&gt; is a pretty old tool, I probably used it already 10+ years ago.&lt;/p&gt;

&lt;p&gt;I recommend this helper to anyone who gets slightly shitty HTML generated from their CMS, static site generator or whatevery is producing such files automatically.&lt;/p&gt;

&lt;p&gt;Usually there is no harm in leaving the HTML pages as they are, as long as they render in most browsers. I like my pages nice and clean. And I wish most programs would come with tidy builtin. I know, I know, I'm such a dreamer.&lt;/p&gt;

&lt;p&gt;Here a snippet how I use &lt;code&gt;fd&lt;/code&gt; and &lt;code&gt;tidy&lt;/code&gt; for my post processing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# from my Makefile:

TIDY_SETTINGS = -q -m -w 0 -i \
                --indent-with-tabs yes \
                --indent-spaces 2 \
                --tab-size 2 \
                --clean yes \
                --join-styles yes

build-tidy-html:
  fd -p public -e html -x sh -c "echo {} &amp;amp;&amp;amp; tidy $(TIDY_SETTINGS) {}" \;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;find all files with extension &lt;code&gt;.html&lt;/code&gt; in the &lt;code&gt;public&lt;/code&gt; folder&lt;/li&gt;
&lt;li&gt;apply command with &lt;code&gt;-x&lt;/code&gt; option;
since I want to do multiple things I have to wrap it with &lt;code&gt;sh -c "…"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tidy&lt;/code&gt; removes all superfluous whitespaces and empty lines, properly indents the tags, and cleans up the inline styles, which is quite handy for the code snippes, because gutenberg (and cobalt) do not use a global/external stylesheet for the syntax highlighting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to learn more about all the possible flags and options, check out the &lt;a href="http://api.html-tidy.org/tidy/quickref_5.6.0.html" rel="noopener noreferrer"&gt;tidy reference&lt;/a&gt; for details and explanations.&lt;/p&gt;

&lt;p&gt;This step helps me a lot getting a consistent output for my whole site, also I will get warnings/errors in case I messed up something in my templates and markup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn30nl0bucdcxj41fw4h3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn30nl0bucdcxj41fw4h3.png" alt="books" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  More about &lt;code&gt;gutenberg&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;In the &lt;a href="https://github.com/Keats/gutenberg#comparisons-with-other-static-site-generators" rel="noopener noreferrer"&gt;gutenberg repo&lt;/a&gt; itself is a nice comparison between &lt;strong&gt;gutenberg&lt;/strong&gt; , &lt;strong&gt;cobalt&lt;/strong&gt; , &lt;strong&gt;hugo&lt;/strong&gt; , and &lt;strong&gt;pelican&lt;/strong&gt;. Since I'm not really interested in non-Rust generators I just keep rephrasing the differences between the first two.&lt;/p&gt;

&lt;p&gt;The only thing gutenberg has not is the &lt;a href="http://cobalt-org.github.io/docs/data.html" rel="noopener noreferrer"&gt;data files&lt;/a&gt; feature of cobalt, which can be seen as flat file databases in YAML, JSON, or TOML format. You would maybe need this if you organize and use a lot of data while site generation step. While it sounds nice it is not something I really need yet.&lt;/p&gt;

&lt;p&gt;What I really appreciate in gutenberg is the wide support of tiny features complementing Markdown based editing. Similar to GitHub wikis you can internally link between documents. Furthermore you get automatically linkable headers and can autogenerate a table of contents (TOC). The template engine is pretty nice (I still need more definable types like &lt;a href="https://github.com/Keats/gutenberg/issues/270" rel="noopener noreferrer"&gt;arrays&lt;/a&gt; and maps, something I could do with liquid based engines). Themes, Aliases, and Pagination are not my main concern yet, but at least the latter could come in handy pretty soon. What I really like are the (custom) shortcodes, I loved it already years ago as a WordPress plugin (or you remember them even from bulletin board/forum software). And the macro system is quite useful when you do base your design on a theme. As mentioned above the stylesheet compilation based on SASS is quite nice, just because splitting up the styles in logical chunks helps to better reason about them.&lt;/p&gt;

&lt;p&gt;Currently there is &lt;a href="https://github.com/Keats/gutenberg/issues/246" rel="noopener noreferrer"&gt;a discussion about custom taxonomies&lt;/a&gt; which is interesting.&lt;/p&gt;

&lt;p&gt;Oh and I totally forgot a tiny but really convenient feature: the automatic page reloading while editing and tweaking. People who also build React apps will love it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Wishlist
&lt;/h4&gt;

&lt;p&gt;I have more items I'd like to see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;probably a plugin system, although I have not an idea yet, how it could look like; but since gutenberg itself seems to be a pretty modular codebase there might be an option to get this going (extension language does not have to be Rust though)&lt;/li&gt;
&lt;li&gt;more "native" blog support (a collection type of blog post based on a common parent directory)&lt;/li&gt;
&lt;li&gt;better page collection access, globally (I have seen that this is done internally already for sitemap and feed generation, we just need to abstract and expose this nicely)&lt;/li&gt;
&lt;li&gt;tera, the template engine could probably benefit from more types and related filters/builtin functions; even with the macro system some things still feel clumsy or nearly impossible&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Migrate all the things
&lt;/h3&gt;

&lt;p&gt;Although gutenberg is not cobalt or Jekyll, the move was not as painful as I initially thought it would be. The template languages are similar enough, syntactically and semantically, so that adjustments were sometimes just search-and-replace, both use curlies for their tags.&lt;/p&gt;

&lt;p&gt;Some of the shortcomings of &lt;a href="https://tera.netlify.com/" rel="noopener noreferrer"&gt;Tera&lt;/a&gt; made some changes slightly more difficult, but overall nothing was totally impossible, or I could just live with a similar but simpler solution for now.&lt;/p&gt;

&lt;p&gt;I didn't like the builtin rss feed generation and it lacks different feed formats anyway, so I work around this by creation my own feed pages in a slightly awkward way: I built "html" pages for each feed format and rename/move the files in a post processing step later. The sitemap generation was a bit more straight forward.&lt;/p&gt;

&lt;p&gt;The biggest part of my time I spent in adopting the blog post directory structure and page traversal for the home page. And while writing this post I have an idea how to make my life easier in the future: since I'm not really using the categories (yet) I could utilize a default category page for simpler blog post listings. Will definitely try this one. (If/when we get custom taxonomies I could shift again to use categories as intended.)&lt;/p&gt;

&lt;p&gt;The organization of post related assets is now a bit different, but maybe even easier/more intuitive. Getting the cover image URL became a bit simpler now.&lt;/p&gt;

&lt;p&gt;Overall the migration was quite fast, less than a day or so. For how it was done I recommend to head over to the &lt;a href="https://github.com/markentier/markentier.tech" rel="noopener noreferrer"&gt;markentier.tech repository&lt;/a&gt; and check out the commit history.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7lte1fugym1985yrx75.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7lte1fugym1985yrx75.png" alt="functions" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Functions, lambdas, and "serverless"
&lt;/h3&gt;

&lt;p&gt;Recently Netlify &lt;a href="https://www.netlify.com/blog/2018/03/20/netlifys-aws-lambda-functions-bring-the-backend-to-your-frontend-workflow/" rel="noopener noreferrer"&gt;opened up their previous beta features&lt;/a&gt;: &lt;strong&gt;Forms, Identity, and Functions (Lambdas).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I have no immediate use for the forms and identity service (yet), but the functions are probably a nice feature to check out first.&lt;/p&gt;

&lt;p&gt;As with most of their tools and support they promise that it will be easy or easier than the underlying solution. And while I haven't even actively played around with AWS Lambda yet, I tested it via Netlify's Functions feature. The free tier is quite limited, of course, but I cannot image a simpler way to get started with functions and lambdas and the whole serverless universe. Although I don't really like this marketing term, since in the end everything is running on some server somewhere. I'd rather think of the evolution after &lt;a href="https://www.heroku.com/" rel="noopener noreferrer"&gt;Heroku&lt;/a&gt;, which never claimed to be serverless, but a platform where most of the painful infrastructure administration is abstracted away. Functions and Lambdas just further narrow down the scope and surface, but still: they are really tiny applications running on managed servers and services, and a little bit more on demand.&lt;/p&gt;

&lt;p&gt;And I have to admit, there is great appeal in such services. Especially for single person projects (like this site), where you want a little bit more than just pure static, but also do not want to invest too much time and effort in setting things up.&lt;/p&gt;

&lt;p&gt;Because in the end all you really want is: &lt;strong&gt;getting things done.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>gutenberg</category>
      <category>cobalt</category>
      <category>migration</category>
      <category>rust</category>
    </item>
  </channel>
</rss>
