Niza Toshpulatov

May 1, 2024

Lo-cal - A Simple Calendar App That Stores Everything Locally

Recently, I decided to add a "Projects" page to my website where I would showcase some of my works outside of my professional career, i.e. side projects and open-source stuff. While I already do have some interesting repositories on my GitHub, I realized that I don’t have many “visual” projects. I needed something that would just pop and bring an awe to the visitor.

TL;DR

The project is an app called lo-cal (https://lo-cal.fly.dev). The source code is available on my GitHub and here is a screenshot 📸

The Idea

I started contemplating on the idea for something that would have strong and pleasant visual aesthetics. First thing that came into my mind was a todo app, but that’s been done a trillion times, so what else?

I’m a productivity enthusiast that conditions himself to constant calendar/todo/reminder tool hopping in the search for a “greatest productivity app”. Sometimes, I do it just for fun and to try new things. Looking at the list of applications installed on my laptop, I decided to try and make a calendar app.

Features

Calendars are a commodity that can be as simple as displaying upcoming and past dates in a nice grid view; or as complex as managing events, todo items, and keeping track of your habits. I wanted to keep it simple, hence I landed on the following core features that I wanted to implement:

  • View calendar dates in a day, week, and month views.
  • Add new events assigned to dates.
  • Have a checkbox to “complete” an event.
  • Update and delete events.
  • Search events.

All wrapped in a beautiful, accessible, and keyboard-friendly UI.

Setup

I wanted the app built over a weekend. That's why I didn’t go with my usual “experimental” approach where I employ a new (for me) framework or library to learn and test something fresh. This time, my focus was on the aesthetics and the product itself. So I decided to go with the good old React SPA setup.

I chose Vite for my tooling and bun as my runtime. I went with the React TypeScript template and added Tailwind on top to speed up the styling and theming process. The final product is deployed to a Fly VM and is available on lo-cal.fly.dev domain.

I started working on the project on a Saturday morning. The first version was ready on Tuesday of the next week. So yeah, I spent more time working on the thing than I initially wanted. Still, I was quite happy with my overall timeline and the quality of the product.

I won’t go into too much detail about the code as it would make this post quite boring and the source code is publicly available on my GitHub anyways. But, I would like to point out one technical detail about the implementation and then discuss the estimations and resource planning side of the project.

A Neat Solution To Theming

I really like how I solved the whole theming feature using the color-mix CSS function to derive the entire color scheme based on a single base color value.


Here is a part of the CSS that does most of the magic:

*,
*::before,
*::after {
  --border-width: var(--base-border-width);
  --color-mix-space: oklab;

  --text-base-color: var(--base-color);
  --text-color-mix: black 60%;
  --text: color-mix(
    in var(--color-mix-space),
    var(--text-base-color),
    var(--text-color-mix)
  );

  --surface-base-color: var(--base-color);
  --surface-color-mix: white 85%;
  --surface: color-mix(
    in var(--color-mix-space),
    var(--surface-base-color),
    var(--surface-color-mix)
  );

  --border-base-color: var(--base-color);
  --border-color-mix: black 40%;
  --border: color-mix(
    in var(--color-mix-space),
    var(--border-base-color),
    var(--border-color-mix)
  );

  --surface-alpha: 1;
  --text-alpha: 1;
  --border-alpha: 0.5;
  --shadow-offset: 4px;

  --text-mix-transparency: calc(100% - var(--text-alpha) * 100%);
  --surface-mix-transparency: calc(100% - var(--surface-alpha) * 100%);
  --border-mix-transparency: calc(100% - var(--border-alpha) * 100%);

  --text-mix: color-mix(
    in srgb,
    var(--text),
    transparent var(--text-mix-transparency)
  );
  --surface-mix: color-mix(
    in srgb,
    var(--surface),
    transparent var(--surface-mix-transparency)
  );
  --border-mix: color-mix(
    in srgb,
    var(--border),
    transparent var(--border-mix-transparency)
  );

  --shadow: 0 0 0 var(--border-width) var(--border-mix),
    var(--shadow-offset) var(--shadow-offset) 0 0 var(--border-mix);

  color: var(--text-mix);
  border-color: var(--border-mix);

  &:is(:focus-visible, .shadow) {
    --border-alpha: 1;

    outline: 0;
    box-shadow: var(--shadow);
  }

  &.surface {
    background-color: var(--surface-mix);
  }

  &.bordered {
    border-width: var(--border-width);
  }

  @media (prefers-color-scheme: dark) {
    --text-color-mix: white 40%;
    --surface-color-mix: black 72%;
    --border-color-mix: black 30%;
  }
}

As you can see, just bunch of color mixing...

Estimations & Resource Planning

Now, I want to share some of my notes I made regarding the estimations in respect to the complexity of the final product. After I released the last update (at least for the foreseeable future), I sat down to take a look at the project from a resource management point of view. Here is what I've learned:

  • If your goal is to build a simple prototype to test an idea: get one senior engineer and give them a couple of days to make it happen.
  • If you want something that you want to present: then let the engineer polish the product for a couple more days.
  • If you want something stable and shipped: give them 2 weeks in total without any major distractions.

These inferences are based on the following data that is derived from the results of the project.

Common modules that every conventional app has/needs

  • Navigation panel
  • Footer or info section
  • Preferences (optional)
  • Status messages
  • Form fields UI library (3rd party or internal)
  • Localization and internationalization

Base benchmark

Core assumptions:
  • Dev team size: 1 senior engineer
  • CRUD entity count: 1
  • CRUD views per entity: 1
  • Entity field could: less than 10
  • UI/UX complexity: Average
  • Mobile UX/UI included: Yes

Estimations*:
  • A prototype with base functionality: 3md
  • Polished MVP: 5md
  • Fully test-covered v1 with logging and monitoring tools: 10md

*- The estimations don’t include the time it takes to brainstorm the idea and create a base specification.

Wrap-up

I enjoyed this experience. It was my first time trying Bun and I must say, it worked with zero issues. I like building something and then staring at it, taking it in, trying to overanalyze it to get some insights. And even if these numbers are not entirely correct, I at least have a new shiny project in my collection

I hope you enjoyed this post at least in some capacity. Stay safe and thanks for reading! 
 

Niza ✌️

About Niza Toshpulatov

Hi! I'm Niza, a web developer, productivity enthusiast, and a tech nerd. I love tinkering with software and in my free time, I like to compose music and write silly poems. Check out my website if you want to learn more about my work.