<?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: Animesh Bulusu</title>
    <description>The latest articles on DEV Community by Animesh Bulusu (@animesh).</description>
    <link>https://dev.to/animesh</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%2F28558%2F1ccc2284-127a-47f6-8ffb-2b81b8775542.jpeg</url>
      <title>DEV Community: Animesh Bulusu</title>
      <link>https://dev.to/animesh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/animesh"/>
    <language>en</language>
    <item>
      <title>Build a Linux CLI app with .NET Core</title>
      <dc:creator>Animesh Bulusu</dc:creator>
      <pubDate>Tue, 14 May 2019 15:37:24 +0000</pubDate>
      <link>https://dev.to/animesh/build-a-linux-cli-app-with-net-core-24jp</link>
      <guid>https://dev.to/animesh/build-a-linux-cli-app-with-net-core-24jp</guid>
      <description>&lt;p&gt;Being a developer and a geek for as long as I can remember, command line tools have always fascinated me. They are relatively easy to create and use than a web app or a desktop app. &lt;/p&gt;

&lt;p&gt;In this post, we will explore how to build a simple command line tool on Linux using .NET Core.&lt;/p&gt;

&lt;p&gt;Create a new console app using the &lt;code&gt;dotnet&lt;/code&gt; cli.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ dotnet new console -o netcore-test-cli
$ code netcore-test-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most command line tools do take user input and perform actions as based on these inputs. &lt;/p&gt;

&lt;p&gt;These inputs come into the &lt;code&gt;Main&lt;/code&gt; function during runtime by the way of the &lt;code&gt;string[] args&lt;/code&gt; parameter. We could simply parse the arguments and their values from the string array &lt;code&gt;args&lt;/code&gt;, but this is a tedious thing for us to do. &lt;/p&gt;

&lt;p&gt;Fortunately, there are many open source libraries to help us do this. One such library is &lt;code&gt;CommandLineParser&lt;/code&gt;. I have used it in a couple of projects and like it.&lt;/p&gt;

&lt;p&gt;Let us add this command line parsing library to the project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ dotnet add package CommandLineParser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get started with CommandLineParser, first we need to specify the types and names of the arguments in the form of an &lt;code&gt;Options&lt;/code&gt; class. Then we need to use a Parser instance to parse the arguments provided.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var result = Parser.Default.ParseArguments&amp;lt;Options&amp;gt;(args)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On successful parsing, we get back a &lt;code&gt;Parsed&amp;lt;Options&amp;gt;&lt;/code&gt; type. Otherwise, we get back a &lt;code&gt;NotParsed&amp;lt;T&amp;gt;&lt;/code&gt; type which is an error collection of type &lt;code&gt;IEnumerable&amp;lt;Error&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The prescribed way to do this is to use the &lt;code&gt;WithParsed&lt;/code&gt; and &lt;code&gt;WithNotParsed&lt;/code&gt; extension methods which take on the respective parsed types and pass the parsed options to further processing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;static void Main(string[] args)
{
    Parser.Default.ParseArguments&amp;lt;Options&amp;gt;(args)
        .WithParsed&amp;lt;Options&amp;gt;(options =&amp;gt; ProcessOptions(options))
        .WithNotParsed&amp;lt;Options&amp;gt;(options =&amp;gt; HandleErrors(options));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the sake of simplicity, let us define two options, &lt;code&gt;Count&lt;/code&gt; and &lt;code&gt;Name&lt;/code&gt;, as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Options
{
    [Option('c', "count", HelpText="The count variable")]
    public int Count { get; set; }

    [Option(HelpText="Name for greeting")]
    public string Name { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to specify the &lt;code&gt;[Option]&lt;/code&gt; attribute on our two properties. This lets &lt;code&gt;CommandLineParser&lt;/code&gt; know which properties are valid for parsing against the user input. Notice that &lt;code&gt;Count&lt;/code&gt; option has &lt;code&gt;c&lt;/code&gt; for its &lt;code&gt;ShortName&lt;/code&gt; and &lt;code&gt;count&lt;/code&gt; for its &lt;code&gt;LongName&lt;/code&gt;. This lets you specify count input as either &lt;code&gt;-c 10&lt;/code&gt; or &lt;code&gt;--count 10&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let us just take the &lt;code&gt;Count&lt;/code&gt; option to generate a sum of all whole numbers upto &lt;code&gt;Count&lt;/code&gt; and display a exit message if the &lt;code&gt;Name&lt;/code&gt; option is specified.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private static void ProcessOptions(Options options)
{
    int sum = 0;
    for (int i = 1; i &amp;lt;= options.Count; i++)
    {
        sum = sum + i;
    }
    Console.WriteLine($"Sum: {sum}");

    if(!string.IsNullOrEmpty(options.Name))
        Console.WriteLine($"Bye, {options.Name}");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To run the program use, &lt;code&gt;dotnet run&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ dotnet run -- --count 10 --name=animesh
Sum: 55
Bye, animesh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to run the compiled program directly, you can run &lt;code&gt;dotnet&lt;/code&gt; command and pass the name of the output dll.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ dotnet bin/Debug/netcoreapp2.2/netcore-test-cli.dll -c 123
Sum: 7626
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make it a proper Linux executable, we need to publish the program with appropriate runtime identifier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ dotnet publish -c 'release' -r 'linux-x64'

Microsoft (R) Build Engine version 15.9.20+g88f5fadfbe for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

  Restore completed in 55.08 ms for /home/animesh/projects/lucubrations/netcore-test-cli/netcore-test-cli.csproj.
  netcore-test-cli -&amp;gt; /home/animesh/projects/lucubrations/netcore-test-cli/bin/release/netcoreapp2.2/linux-x64/netcore-test-cli.dll
  netcore-test-cli -&amp;gt; /home/animesh/projects/lucubrations/netcore-test-cli/bin/release/netcoreapp2.2/linux-x64/publish/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This publishes the program to folder &lt;code&gt;bin/Release/netcoreapp2.2/linux-x64&lt;/code&gt;. Check it out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ll bin/Release/netcoreapp2.2/linux-x64

total 1552
-rwxrw-rw- 1 animesh animesh 692128 Jan 18 03:54 libhostfxr.so
-rwxrw-rw- 1 animesh animesh 712624 Jan 18 03:54 libhostpolicy.so
-rwxrw-rw- 1 animesh animesh 106912 Mar  9 15:29 netcore-test-cli
-rw-r--r-- 1 animesh animesh  37340 Mar  9 15:29 netcore-test-cli.deps.json
-rw-r--r-- 1 animesh animesh   5632 Mar  9 15:29 netcore-test-cli.dll
-rw-r--r-- 1 animesh animesh    928 Mar  9 15:29 netcore-test-cli.pdb
-rw-r--r-- 1 animesh animesh    206 Mar  9 15:29 netcore-test-cli.runtimeconfig.dev.json
-rw-r--r-- 1 animesh animesh     26 Mar  9 15:29 netcore-test-cli.runtimeconfig.json
drwxr-xr-x 2 animesh animesh  12288 Mar  9 15:29 publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the file attributes on the third item &lt;code&gt;netcore-test-cli&lt;/code&gt; in the directory listing above. It has file attributes &lt;code&gt;-rwxrw-rw-&lt;/code&gt;, which means it is a Linux executable. This became possible as we specified the &lt;code&gt;linux-x64&lt;/code&gt; runtime identifier. &lt;/p&gt;

&lt;p&gt;We can run the executable just as before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd bin/Release/netcoreapp2.2/linux-x64

$ ./netcore-test-cli 

Sum: 0

$ ./netcore-test-cli -c 100

Sum: 5050
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both parameters specified:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./netcore-test-cli -c 1000 --name animesh

Sum: 500500
Bye, animesh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Specifying the &lt;code&gt;--help&lt;/code&gt; option shows all possible options for user input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./netcore-test-cli --help

netcore-test-cli 1.0.0
Copyright (C) 2019 netcore-test-cli

  -c, --count    The count variable

  --name         Name for greeting

  --help         Display this help screen.

  --version      Display version information.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the end of this post. We have seen how to build a basic command line application with .NET Core.&lt;/p&gt;

&lt;p&gt;All code for this post is available on my &lt;a href="https://gitlab.com/lucubrations/netcore-test-cli"&gt;gitlab repository&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally posted on my blog @ &lt;a href="https://animesh.blog/build-a-linux-cli-app-with-net-core/"&gt;https://animesh.blog/build-a-linux-cli-app-with-net-core/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dotnetcore</category>
      <category>linux</category>
    </item>
    <item>
      <title>Note taking with QOwnNotes</title>
      <dc:creator>Animesh Bulusu</dc:creator>
      <pubDate>Thu, 09 May 2019 19:59:37 +0000</pubDate>
      <link>https://dev.to/animesh/note-taking-with-qownnotes-4iop</link>
      <guid>https://dev.to/animesh/note-taking-with-qownnotes-4iop</guid>
      <description>&lt;p&gt;I have written a few days back about the &lt;a href="https://dev.to/animesh_bulusu/search-for-the-near-perfect-note-taking-software-2phi/"&gt;search&lt;/a&gt; for near perfect note taking software. I would like to provide a short rundown on the features of QOwnNotes in this post.&lt;/p&gt;

&lt;p&gt;QOwnNotes is a open source notepad with markdown support and hierarchical notes. Let us look at some features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Markdown support
&lt;/h2&gt;

&lt;p&gt;Markdown is the default plaintext editing option. Files can be written in plain text and saved as markdown files. This is crucial for me because my fundamental goal is to create a personal knowledge base accessible to me in the browser. It may either be public accessible website or a website local to my machine. &lt;/p&gt;

&lt;p&gt;QOwnNotes has a markdown preview panel which lets us view our rendered markdown live next to the editor panel. With the live preview present, I have no use for other Linux desktop markdown editors like typora, abricotine, retext and remarkable.&lt;/p&gt;

&lt;p&gt;This is how the editor and preview look like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.imgur.com/qze0U4d.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9i83wcxvcc3u8ppn7wu3.png" alt="Picture of QOwnNotes editor and preview panels" width="800" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Hierarchical notes
&lt;/h2&gt;

&lt;p&gt;Let us say, we have the following structure with some notes. Note that the files &lt;code&gt;learning-resources.md&lt;/code&gt; and &lt;code&gt;tutorials.md&lt;/code&gt; are at the root level of the &lt;code&gt;learning&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;└── learning
    ├── dotnet
    │   ├── designpatterns.md
    │   ├── generics.md
    │   └── solid.md
    ├── javascript
    │   ├── angular.md
    │   ├── es6.md
    │   └── react.md
    ├── learning-resources.md
    └── tutorials.md

3 directories, 9 files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be able to view this structure inside QOwnNotes&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create a note folder &lt;code&gt;testing&lt;/code&gt; from the settings dialog&lt;/li&gt;
&lt;li&gt;select path to &lt;code&gt;learning&lt;/code&gt; folder as the note folder path&lt;/li&gt;
&lt;li&gt;select Use note subfolders option&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is how it looks. As you can see here, the folder structure that we created before is showing up as is. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.imgur.com/7G9vFbo.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvwklpw4pfqxj1muy2dev.png" alt="Picture of QOwnNotes note folder hierarchy" width="800" height="570"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the top panel, we see the folders and &lt;code&gt;learning&lt;/code&gt; folder is selected. In the bottom panel, we see all the files in the &lt;code&gt;learning&lt;/code&gt; folders, including its subfolders. If we now select the &lt;code&gt;dotnet&lt;/code&gt; folder, in the bottom panel, we will only see the files from this folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.imgur.com/2mdgNLH.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F668nypw1gdyhiyuxkz7o.png" alt="Picture of QOwnNotes note folder hierarchy" width="800" height="570"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Apart from this display of folder hierarchy, we can also apply tags to files. All tags created across the the entire note folder called &lt;code&gt;testing&lt;/code&gt; will show up in the middle.&lt;/p&gt;

&lt;p&gt;Here is an example from one of my note folders with two tags, &lt;code&gt;csharp&lt;/code&gt; and &lt;code&gt;linux&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.imgur.com/ih2DFbF.gif" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk5qoy3bfiptkwy91fmt0.gif" alt="Picture of QOwnNotes note folder hierarchy" width="800" height="570"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Note folders
&lt;/h2&gt;

&lt;p&gt;We can create multiple note folders in the settings dialog. Each note folder will act as a workspace. Notes in a note folder will only show up in that note folder.&lt;/p&gt;

&lt;p&gt;Create all your note folders and designate one note folder as active note folder. To view and switch between note folders, there is a panel available. It might not be displayed by default or if you changed to Minimal layout mode. To re-enable it, select the following menu item.&lt;/p&gt;

&lt;p&gt;Window &amp;gt; Panels &amp;gt; Note folder panel&lt;/p&gt;

&lt;p&gt;This is particularly useful when we have many text files in a complex folder hierarchy.&lt;/p&gt;

&lt;p&gt;In QOwnNotes, confusingly enough there is a feature called workspaces, but it is for different layouts we would like to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keyboard accessibility
&lt;/h2&gt;

&lt;p&gt;Most of the menu items are keyboard accessible. I use few shortcuts often.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add tag to note: Alt+Shift+A&lt;/li&gt;
&lt;li&gt;Switch to next note: Alt+Down&lt;/li&gt;
&lt;li&gt;Switch to previous note: Alt+Prev&lt;/li&gt;
&lt;li&gt;Select note folder: Ctrl+Alt+;&lt;/li&gt;
&lt;li&gt;Insert current time: Ctrl+T&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;There are other features you will find useful and more.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;note linking&lt;/li&gt;
&lt;li&gt;note splitting&lt;/li&gt;
&lt;li&gt;built-in todo list&lt;/li&gt;
&lt;li&gt;distraction free mode&lt;/li&gt;
&lt;li&gt;encryption&lt;/li&gt;
&lt;li&gt;panel unlocking&lt;/li&gt;
&lt;li&gt;scriptability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;QOwnNotes has replaced markdown editors and even geany for editing markdown files. I have been using it extensively this past week and recommend it very highly. If you need a quality markdown notepad for maintaining your personal knowledge base, give it a try.&lt;/p&gt;

&lt;p&gt;How do you pronounce QOwnNotes? For something that I am going to use every waking moment, I need to know this. Some possibilities include — Queue Own Notes, Cone Notes, Connotes, QNote, QNotes. Let us try to find out.&lt;/p&gt;

&lt;p&gt;Learn more about QOwnNotes on its &lt;a href="https://github.com/pbek/QOwnNotes/" rel="noopener noreferrer"&gt;github repository&lt;/a&gt;. To play with markdown, use &lt;a href="https://daringfireball.net/projects/markdown/dingus" rel="noopener noreferrer"&gt;Dingus&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>review</category>
    </item>
    <item>
      <title>Search for the near-perfect note taking software</title>
      <dc:creator>Animesh Bulusu</dc:creator>
      <pubDate>Mon, 06 May 2019 17:14:04 +0000</pubDate>
      <link>https://dev.to/animesh/search-for-the-near-perfect-note-taking-software-2phi</link>
      <guid>https://dev.to/animesh/search-for-the-near-perfect-note-taking-software-2phi</guid>
      <description>&lt;p&gt;I have a lot of information stored in a number of text files littered all over my workstations from as early as 2007. For a while, I would just place them in dropbox and forget about them. I had hundreds of programming notes, error logs, dotfiles, questions to be asked on stackoverflow, potential blog posts and other miscellany.&lt;/p&gt;

&lt;p&gt;Last month, as a part of a larger effort, I decided to consolidate all these notes into a personal knowledge base. Thus began my hunt for good note taking software. &lt;/p&gt;

&lt;p&gt;My requirements were simple. The program must support the following features.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Plain text editing of markdown files&lt;/li&gt;
&lt;li&gt;Hierarchical notes&lt;/li&gt;
&lt;li&gt;Workspaces&lt;/li&gt;
&lt;li&gt;Keyboard accessible&lt;/li&gt;
&lt;li&gt;Customizable (optional)&lt;/li&gt;
&lt;li&gt;In-built Server (optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I conducted this exercise of finding such software several times unsuccessfully over the last decade. So, a few familiar names popped up on my mind and searches.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/WikidPad/WikidPad"&gt;Wikidpad&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I have used it extensively about 8 years back for a good amount of time and remember having lots of notes while working in a startup. I cannot recollect the reason why I did not continue to use it.  It does not fit my current requirements, but curious users should take a peek at it.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://zim-wiki.org/"&gt;Zim&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This is a very mature program and is available in apt. It ticked every checkbox except markdown format.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://joplin.cozic.net/"&gt;Joplin&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Joplin is Electron based, so it is not my cup of tea. Only VSCode seems to be whitelisted in my &lt;code&gt;electron_bad_for_laptop&lt;/code&gt; policy. At least two instances of VSCode are always running on my laptop. My note taking software is the first program after terminal, to always stay opened. I did not explore it further. Again, curious users should try it.&lt;/p&gt;

&lt;h2&gt;
  
  
  EverNote
&lt;/h2&gt;

&lt;p&gt;I have used EverNote briefly for about a day almost 8 years back. It did not grow on me. &lt;/p&gt;

&lt;h2&gt;
  
  
  OneNote
&lt;/h2&gt;

&lt;p&gt;OneNote is not cross-platform. This rules it out completely. It also has a proprietary format and is not great at keyboard accessibility. However, it is a great program. I use it regularly on my Windows laptop and wherever I have a subscription of Office365.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/pbek/QOwnNotes/"&gt;QOwnNotes&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This is one name that felt familiar. Hacker News is where I seemed to have found this recommendation. I did not like its webpage design then and do not like it now. However, that is not what I am here after. As I learnt, visceral reactions are not really trustworthy.&lt;/p&gt;

&lt;p&gt;This time I wanted to give it a try given my renewed fascination of QT based software. It ticked all boxes except the server one. I don't really care for it at the moment as I have other different plans for that.&lt;/p&gt;

&lt;p&gt;QOwnNotes supports &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;markdown as the default plaintext editing option&lt;/li&gt;
&lt;li&gt;keyboard shortcuts&lt;/li&gt;
&lt;li&gt;hierarchical notes &lt;/li&gt;
&lt;li&gt;workspaces in the form of note folders&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are few other great features.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customizability - Most of the editor and the panels are completely customizable.&lt;/li&gt;
&lt;li&gt;Note encryption - Always good to have encryption.&lt;/li&gt;
&lt;li&gt;Markdown preview panel - With this, I have no use for other markdown editors on Linux like typora, abricotine, retext and remarkable.&lt;/li&gt;
&lt;li&gt;Scripting - An opportunity for stepping into QML.&lt;/li&gt;
&lt;li&gt;Auto update - It is possible to auto update from the central server, but I added the accompanying repository to my sources, so that, I could get the latest on &lt;code&gt;apt update&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Vim mode - Not for me, but good to have it.&lt;/li&gt;
&lt;li&gt;Nextcloud support - Not for me, but good to have an alternative cloud sync option.&lt;/li&gt;
&lt;li&gt;Versioning - Not for me, but great to have. Also, I have not figured it out fully yet, but since my notes exist inside Dropbox, this is not a must have at the moment.&lt;/li&gt;
&lt;li&gt;Cross platform - I don't use this feature yet, but it is great to have it, if you need it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It took me around two whole weeks to organize all my files into a single folder called notes.&lt;/p&gt;

&lt;p&gt;I love this software and wholeheartedly recommend it. I will explore these features in detail in the next post.&lt;/p&gt;

</description>
      <category>productivity</category>
    </item>
    <item>
      <title>Improving usability of static blogs</title>
      <dc:creator>Animesh Bulusu</dc:creator>
      <pubDate>Sat, 04 May 2019 20:43:00 +0000</pubDate>
      <link>https://dev.to/animesh/improving-usability-of-static-blogs-ib</link>
      <guid>https://dev.to/animesh/improving-usability-of-static-blogs-ib</guid>
      <description>&lt;p&gt;There are three main things to focus to improve the usability of a static blog. Though I have written this with Lektor in mind, most of techniques are generic to any static blog generator.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Performance&lt;/li&gt;
&lt;li&gt;SEO&lt;/li&gt;
&lt;li&gt;Accessibility &lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Static blogs are fast by default. We can improve the page load times further by implementing some  optimization techniques. I use &lt;a href="https://varvy.com"&gt;Varvy&lt;/a&gt; to identify potential improvements. A few other popular websites you may find useful are listed.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/speed/pagespeed/insights/"&gt;Google Pagespeed Insights&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gtmetrix.com/"&gt;GTMetrix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.webpagetest.org/"&gt;WebPageTest&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev"&gt;web.dev&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Common suggestions, which I apply to my blog, include &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;minifying static resources &lt;/li&gt;
&lt;li&gt;enabling gzip compression&lt;/li&gt;
&lt;li&gt;using HTTPS&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Minification
&lt;/h3&gt;

&lt;p&gt;Minification is the process of compressing static files to reduce the file download size. For example, the following JavaScript snippet is 540 bytes.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var myModule = (function() {
    'use strict';

    var _privateProperty = 'Hello World';

    function _privateMethod() {
        console.log(_privateProperty);
    }

    return {
        publicMethod: function() {
            _privateMethod();
        }
    };
}());

myModule.publicMethod();                    // outputs 'Hello World'   
console.log(myModule._privateProperty);     // is undefined protected by the module closure
myModule._privateMethod();                  // is TypeError protected by the module closure
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When compressed, it is shrunk to 215 bytes.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var myModule=function(){"use strict";function o(){console.log(e)}var e="Hello World";return{publicMethod:function(){o()}}}();myModule.publicMethod(),console.log(myModule._privateProperty),myModule._privateMethod();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is a 60% reduction in download size or bandwidth costs, however you would like to call it.&lt;/p&gt;

&lt;p&gt;I use an unofficial plugin &lt;a href="https://github.com/pietroalbini/lektor-minify"&gt;lektor-minify&lt;/a&gt; to do this.  To use this plugin, install it with the lektor plugin command. &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;code&gt;lektor plugins add lektor-minify&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;To see the minification in action, run the following command.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;code&gt;lektor build -f minify:html,css,js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Open the output folder and check the HTML files. All static files are compressed. For css and js files, associated &lt;code&gt;.min.css&lt;/code&gt; and &lt;code&gt;.min.js&lt;/code&gt; files are created and for html files, the content of the html file is minified into the same file. Separate &lt;code&gt;.min.html&lt;/code&gt; file will not be generated.&lt;/p&gt;

&lt;p&gt;I include the above line in &lt;code&gt;gitlab-ci.yml&lt;/code&gt;. It is executed just before the built content is deployed to the server.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;image: python:3.6
pages:
 script:
 - pip install lektor
 - lektor build --output-path public -f minify:html,css,js
 artifacts:
   paths:
   - public
 only:
 - master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  GZIP compression
&lt;/h3&gt;

&lt;p&gt;Apart from minifying files, we can take care of one more level of compression. Gzipping a file compresses it to &lt;code&gt;.gz&lt;/code&gt; file format which browsers can unpack on receipt.&lt;/p&gt;

&lt;p&gt;If we are using a web server to serve our blog, then we can tell the web server to gzip our pages and return to the client. Since I use gitlab pages for deployment, gzipping of all content is done when the blog is being deployed. &lt;/p&gt;

&lt;p&gt;To do this, I add the following shell command into our &lt;code&gt;gitlab-ci.yml&lt;/code&gt;.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gzip -k -6 -r public
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here is the full example.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;image: python:3.6

pages:
  script:
  - pip install lektor
  - lektor build --output-path public -f minify:html,css,js
  - gzip -k -6 -r public
  artifacts:
    paths:
    - public
  only:
  - master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will run the &lt;code&gt;gzip&lt;/code&gt; command after the build step is complete. Now, when a client requests for a page at the URL, &lt;code&gt;/blogpost-url-123/&lt;/code&gt;, server returns &lt;code&gt;/blogpost-url-123/index.html.gz&lt;/code&gt;. Browser understands this file and does an additional step of unpacking it before rendering it.&lt;/p&gt;

&lt;p&gt;We can check the amount of compression using the website &lt;a href="https://checkgzipcompression.com"&gt;checkgzipcompression.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JWsjS-FA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/Liwirph.png" class="article-body-image-wrapper"&gt;&lt;img alt="Image of gzip compression checker website" src="https://res.cloudinary.com/practicaldev/image/fetch/s--JWsjS-FA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/Liwirph.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As is evident here, I got 70% savings on one of my large blog posts.&lt;/p&gt;

&lt;p&gt;While these are the main things I implement for improving performance, I also implement a few other things based on the insights website optimization tools provide. One of them I use often is using compressed images.&lt;/p&gt;

&lt;h2&gt;
  
  
  SEO
&lt;/h2&gt;

&lt;p&gt;SEO, which stands for Search Engine Optimization, is an optimization that improves the visibility of your website or blog posts on various search engines like Google, Bing and DuckDuckGo. An article written on a specific topic will reach more people when it is optimized for search engines.&lt;/p&gt;

&lt;p&gt;SEO is a vast and an ever changing topic and cannot be comprehensively covered in this article. However, let us look at some popular techniques including&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;adding meta information&lt;/li&gt;
&lt;li&gt;structuring content&lt;/li&gt;
&lt;li&gt;using HTTPS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lektor does not provide any SEO options by default. Let us take a stab at these three things.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Meta information
&lt;/h3&gt;

&lt;p&gt;There are variety of ways to provide metadata or meta information about an article or a webpage.&lt;/p&gt;

&lt;h4&gt;
  
  
  Meta tags
&lt;/h4&gt;

&lt;p&gt;There are many meta tags, but the following are the most important, IMHO.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;title - like title tag, but for crawlers&lt;/li&gt;
&lt;li&gt;author - indicate the author of the content&lt;/li&gt;
&lt;li&gt;copyright - copyright content on your webpage&lt;/li&gt;
&lt;li&gt;description - summarize the content of the webpage&lt;/li&gt;
&lt;li&gt;keywords - like tags, but for crawlers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In case of Lektor, I create a property on the associated model and then use it on the template. For example, to have a meta tag for description on articles, first I add a field on the &lt;code&gt;blog-post.ini&lt;/code&gt; model:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[fields.metadesc]
label="Meta Tag: Description"
type = string
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;metadesc&lt;/code&gt; field is available for use in templates. So I add the following markup in the head of relevant template.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;meta name="description" content="{{ this.metadesc }}" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Since this is a field, it appears on the admin.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.imgur.com/TkPuKa8.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Gg-NfMhs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/TkPuKa8.png" alt="Picture of lektor admin showing the meta description field"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Meta Description field in Lektor Admin&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here is a full example with all the above meta tags.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;meta name="title" content="Improving usability of static blogs | Animesh Bulusu" /&amp;gt;
&amp;lt;meta name="author" content="Animesh Bulusu" /&amp;gt;
&amp;lt;meta name="copyright" content="Animesh Bulusu" /&amp;gt;
&amp;lt;meta name="description" content="Optimizing my static blog for SEO and performance" /&amp;gt;
&amp;lt;meta name="keywords" content="seo, optimization, optimize, static blog, lektor, performance optimization, meta tags, metadata, 2019, accessibility, performance, animesh bulusu, animesh.blog, animesh bulusu blog" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: Meta tag keywords is &lt;a href="https://yoast.com/meta-keywords/"&gt;not considered&lt;/a&gt; &lt;a href="https://datadrivenlabs.io/blog/meta-keyword-tag-google-2018/"&gt;useful&lt;/a&gt; anymore. I was not aware of this as I added these features in between 2016 and 2017. I left it in case one would like know about it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Canonical URLs
&lt;/h4&gt;

&lt;p&gt;A canonical URL is required to indicate that a particular webpage is the original source. This can be useful for two things.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When we cross post our content into other content aggregators like DEV.TO and Medium, it will add a link at the end of the article, something to the effect of "This blog post is originally from: &lt;a href="https://animesh.blog/improving-usability-of-static-blogs"&gt;https://animesh.blog/improving-usability-of-static-blogs&lt;/a&gt;"&lt;/li&gt;
&lt;li&gt;When we have multiple copies of an article, we could designate one article as canonical. This way search engines know that the article with canonical URL is the original and do not punish the article.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the markup, I add the following tag. &lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;link rel="canonical" href="{{'.'|url(external=true)}}" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This generates the absolute URL for the current page and is rendered as follows.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;link rel="canonical" href="https://animesh.blog/improving-usability-of-static-blogs/" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Robots.txt
&lt;/h4&gt;

&lt;p&gt;Add this file at the root of your directory to indicate whether you want your content to be indexed or not. This is in my robots.txt file. First line tells the crawler that my blog is available for indexing. The second line tells the crawler that I disallow crawling of the static folder.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User-agent: *
Disallow: /static/*
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Sitemap
&lt;/h4&gt;

&lt;p&gt;In the robots.txt file, add a reference to a sitemap.xml file like this.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sitemap: https://animesh.blog/sitemap.xml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This sitemap.xml file can be generated when the site is built. I use this sitemap from Lektor documentation. &lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;urlset&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.sitemaps.org/schemas/sitemap/0.9"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  {%- for page in [site.root] if page != this recursive %}
  &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&amp;lt;loc&amp;gt;&lt;/span&gt;{{ page|url(external=true) }}&lt;span class="nt"&gt;&amp;lt;/loc&amp;gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
  {{- loop(page.children) }}
  {%- endfor %}
&lt;span class="nt"&gt;&amp;lt;/urlset&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It recursively generates the links for all the webpages on my blog, which is useful for crawlers.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;urlset&amp;gt;
  &amp;lt;url&amp;gt;
    &amp;lt;loc&amp;gt;https://animesh.blog/&amp;lt;/loc&amp;gt;
  &amp;lt;/url&amp;gt;
  &amp;lt;url&amp;gt;
    &amp;lt;loc&amp;gt;https://animesh.blog/improving-usability-of-static-blogs/&amp;lt;/loc&amp;gt;
  &amp;lt;/url&amp;gt;
&amp;lt;/urlset&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It is also helpful to add a link to a generated sitemap the main page of your blog. Checkout my &lt;a href="https://animesh.blog/sitemap.html"&gt;sitemap&lt;/a&gt; page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Structuring content
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Headings
&lt;/h4&gt;

&lt;p&gt;Use heading tags such as h1, h2 and h3, but remember to have only one H1 tag per page. The h1 tag should contain the words most relevant to your article.&lt;/p&gt;

&lt;h4&gt;
  
  
  Semantic Tags
&lt;/h4&gt;

&lt;p&gt;Use semantic tags such as the following to structure your content.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;header&lt;/li&gt;
&lt;li&gt;main&lt;/li&gt;
&lt;li&gt;nav&lt;/li&gt;
&lt;li&gt;footer&lt;/li&gt;
&lt;li&gt;article&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Friendly URL
&lt;/h4&gt;

&lt;p&gt;The gist of this technique is to have a URL that is relevant to the article.&lt;/p&gt;

&lt;p&gt;Let us say you are writing an article on how to learn react.js&lt;/p&gt;

&lt;p&gt;A URL like&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mydomain.tld/how-to-learn-react/"&gt;https://mydomain.tld/how-to-learn-react/&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;is a better URL than &lt;/p&gt;

&lt;p&gt;&lt;a href="https://mydomain.tld/article?id=203"&gt;https://mydomain.tld/article?id=203&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using HTTPS
&lt;/h3&gt;

&lt;p&gt;This is an area that feels like it is a performance thing rather than a SEO thing. It is a performance aspect in reality. However, the use of HTTPS has become common place these days due to efforts of Google. Originally, search engines have &lt;a href="https://webmasters.googleblog.com/2014/08/https-as-ranking-signal.html"&gt;started&lt;/a&gt; ranking sites without HTTPS lower. Browsers vendors have more recently begun highlighting the sites not using HTTPS as "Not secure". Due to this, having HTTPS on a website has become an SEO thing.&lt;/p&gt;

&lt;p&gt;Among other things HTTPS mainly guarantees two things, identity and data integrity.&lt;/p&gt;

&lt;p&gt;An SSL certificate tells the browser that the information it receives is indeed from the originating party. A HTTPS connection prevents a hijacker from reading the data transferred from the server to user. A HTTPS connection also prevents the hijacker from tampering the data in transit. This type of attack is called &lt;a href="https://en.wikipedia.org/wiki/Man-in-the-middle_attack#Example"&gt;MITM&lt;/a&gt;. HTTPS makes it harder to perform MITM attacks.&lt;/p&gt;

&lt;p&gt;There is a class of malicious activity whereby an ISP &lt;a href="https://www.infoworld.com/article/2925839/net-neutrality/code-injection-new-low-isps.html"&gt;injects&lt;/a&gt; code or ads into your connections. This is possible if your connection to a website is unencrypted. HTTPS prevents this.&lt;/p&gt;

&lt;p&gt;Setting up an SSL certificate has been easier for at least two years now, thanks to Let's Encrypt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility
&lt;/h2&gt;

&lt;p&gt;Accessibility is a development practice that makes the browsing of a webpage easier for people with disabilities. Again like SEO, accessibility is an ocean of a topic. It is, however, of great importance that many organizations make it a legal requirement to make their web products accessible. So, IMHO, it is worth a consideration. &lt;/p&gt;

&lt;p&gt;My personal opinion is that it is a &lt;a href="https://gomakethings.com/building-accessible-websites-and-apps-is-a-moral-obligation/"&gt;moral obligation&lt;/a&gt; and should be taken into consideration when building a new blog. It is easier to fall into the trap of thinking that your blog isn't as important. Moreover adding all the extra stuff just takes time, doesn't it? It does take time, but it is your blog. There is no rush to implement everything right this moment. Keep adding things as you find time. Boy scout rule does apply here very well. &lt;/p&gt;

&lt;p&gt;Here are a few things I have managed to implement, though not to my full satisfaction. All of these are quite easy to implement. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;skiplinks - these are indicators of where the actual content starts&lt;/li&gt;
&lt;li&gt;alt text for images and videos - screen readers would use this information to announce what the media is about&lt;/li&gt;
&lt;li&gt;page title - this is useful for a number of scenarios&lt;/li&gt;
&lt;li&gt;logical structure - the navigability of the site when structured logically provides a better experience to users using a screen reader.&lt;/li&gt;
&lt;li&gt;page language&lt;/li&gt;
&lt;li&gt;meaningful tab order - having this maintains a linear or logical navigation for users who are keyboard-only or use screen readers &lt;/li&gt;
&lt;li&gt;focus indication - indication of where the focus is when content is accessed by keyboard&lt;/li&gt;
&lt;li&gt;color contrast - users with poor eyesight or low vision will have a better experience if this is taken into account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rather than recommending a specific learning resource for these individual items, I would encourage reading the &lt;a href="https://www.wuhcag.com/wcag-checklist/"&gt;WCAG checklists&lt;/a&gt; and the levels of compliance first and only then reach for learning on specific items. &lt;/p&gt;

&lt;p&gt;I have recently become aware of this checklist as part of a work project. Only then, I have been able to form an internal expression on how these seemingly small things do wonders to your readers. This helped me pull this article out of draft status after more than a year.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ending note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To improve the usability of a static blog further, one could invest time to &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;implement responsive web design&lt;/li&gt;
&lt;li&gt;convert to a PWA&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A PWA might be an overkill for most static blogs. However, responsive web design is a must-have if you are building your blog today. I have implemented responsive design with media queries and some flexbox. &lt;/p&gt;

&lt;p&gt;Feel free to inspect, view-source or view the full source on &lt;a href="https://gitlab.com/aniemsh/aniemsh.gitlab.io"&gt;gitlab&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>staticblog</category>
      <category>usability</category>
      <category>a11y</category>
      <category>seo</category>
    </item>
  </channel>
</rss>
