bkh.dev / blog / nefis-food

Project: Nefis Food

Website: nefisfood.kz (prev: nefis-fork.vercel.app)
GitHub: 🔒

Nefis Food is a company founded by my relatives which produces sugar, coffee and other HoReCa related products. They needed a digital presence for their company in the form of a website, and I helped them with that.

Nefis Food had a website before, which was created using some no-code tool. But my relatives wanted a refresh, and they’ve recently updated the company logo so website needed update as well.

About the project

They had a few wishes about the end result of the website:

As for everything else, I had the freedom of choice.

So, after a few discussions with them, I began exploring the ways to build the website. And firstly, when it comes to choosing the correct way to build something it maybe best to start thinking about what we are trying to achieve and what are the requirements/constraints.

Similar ideas I’ve learned from this article: https://philipcdavis.com/apple.

Start

So, what are the requirements/constraints? what is the end goal?

And also some things to consider:

The company is based in Kazakhstan, more specifically in suburbs of Shymkent, where generally internet connection quality is poor. And, the website acting as a digital storefront, being visited by potential customers should load fast, be responsive, so visitors do not leave because of slow loading times. This also impacts SEO
So:

Website will not be updated frequently, the types of content also won’t change much, so optimizing for easy to make updates and maintainability is helpful because in the future, when something needs to be updated or added, it should be easy to open project repo, understand the written logic/code and make necessary changes.
So:

And lastly, the cost of managing website should be as low as possible, it should not cost much to run.
So:

Technical choices

With these almost-defined requirements, it was pretty clear that website will be fully static, being fully static has its own tradeoffs, but in this very case, it was the right fit.

At work we mostly build client-side rendered websites, single page applications, and I was curious to try out this way of building websites. Because I’ve known that in the early days of web websites were mostly static, browser, web and html APIs were implemented to suit static website needs(form and anchor elements behavior for example) and yeah it was interesting to learn how it feels to build in an old school way.

I mentioned in my previous post, that I maintain a list of tools/technologies to try out, and one of them was Astro. Based on user reviews(twitter, hacker news etc.) Astro seemed just right for all the requirements!

Design

Colors and fonts really make a difference when it comes to delivering a message/communicating with user, they can set the mood, create associated feelings. Oh, and of course copywriting too, but I dont have enough experience in that yet, so the copywriting part could’ve been improved(which is also a part of reason that I’m writing this blog post xD)

So, after some brainstorming in Figma and doing some research following *vibes*, *associations* felt good:

Development

So Astro comes with some goodies, like <Image /> and <Picture /> components which help with image optimization, jsx-like syntax, pre-fetching/rendering techniques, i18n routing, client directives, managing content with content collections. All of these features were used in project and led to a nice development experience.

  1. Image optimization: for images <Picture /> was such a lifesaver, it automatically converted original images to avif and webp formats at build-time, and also generated different sizes of images for different screen sizes. This was a huge win for performance, because images are the biggest part of the website, and they are the biggest bottleneck for performance. The main part is that it was all automatic, I just had to specify a few props and that’s it.
<Picture
  src={img}
  widths={[240, 540, 720, 1080, 1200, img.width]}
  formats={["avif", "webp"]}
  alt=""
/>
  1. Pre-fetching/rendering: Astro provides a way to prefetch a page(on hovering a link for that page for example) so when user hovers a link, the page is fetched and when user clicks on it, the page is already there, no loading time.

  2. Multiple language support: Astro has a built-in i18n routing configuration, with it you can structure your project files in an understandable way. With that, I was able to support multiple languages, have different routes for different languages, and also manage different content for different languages.

  3. Content collections: this feature helped to manage content in a structured way, it is important to have a clear structure for content, because it can get messy quickly as you scale. Using a config file(with Typescript, Zod support) I was able to define strict shapes/rules/types for content.

  4. Client directives: I’ve used React components for some parts of the website. Client directives helped to choose how to render those components, when for example I needed to render a component only on client-side, or SSR it.

So basically Astro’s features hit right on the spot for the requirements.

Font optimization

Did you know that you can load only needed subset of a font? For example, if you only need cyrillic characters, you can load only those characters, and not the whole font. If you use Google Fonts, you can set subset parameter in the url, and it will load only those characters. Example:

https://fonts.googleapis.com/css?family=Roboto+Mono&subset=cyrillic

Moreover, remember that I mentioned I needed some decorative, handwritten-ish fonts for special occasions? I needed decorative font only for this headline “We make the world sweeter”. So I was loading whole font for just one headline, but reading Google Fonts docs I found out that you can load only needed characters of a font, like this:

https://fonts.googleapis.com/css?family=Inconsolata&text=Hello%20World

In this case, only characters “Hello World” will be loaded, and not the whole font. Docs say - “In some cases, this can reduce the size of the font file by up to 90%.”(link)

SEO

For SEO, Astro has a sitemap integration, which generates a sitemap.xml and robots.txt file at build-time. In addition to that, I used @astrolib/seo package to generate page metadata, like title, description, OG tags, and other meta tags. Also, during development I learned about structured data, which is a way to provide search engines with information about the content on your website. It should be added to the page as a script tag with relevant data.

Hosting

The TLD of the website is ‘.kz’, which is the top-level domain for Kazakhstan. By law, websites with ‘.kz’ TLD should be hosted in Kazakhstan. Initially, I wanted to host the website on Vercel or Cloudflare, but because of this constraint, I had to look for hosting providers in Kazakhstan. I found a hosting provider that was offering cheap hosting(~$1/month) suited for static websites, this option had some limitations too, like: no ssh access; limited disk space/capacity; limited server-side configurations. But this hosting provider had one handy feature - remote git repository deployment, so I was able to push changes to the git repository(GitHub) and the website was automatically updated via webhooks. Also one thing I had to do before pushing changes was to build the project locally and push the build folder to the repository.

Google Alerts

And one lil thing that I did was setting up Google Alerts for the company name, so I can get notified when something is posted about the company on the internet.

Analytics

For analytics, I wanted to self-host Plausible Analytics(thought it would be an interesting experience!), but because of time constraints, I had to delay it. And I ended up using Cloudflare Web Analytics.

Contact Form

The website has a contact form, where visitors can send messages to the company. To optimize the cost, I created an HTTP endpoint in Val Town that receives form data and sends it to the company’s email through Resend’s SDK. With Resend I was able to configure the look of an email. This whole implementation was very simple and straightforward, both Val Town and Resend provide excellent DX.

OG Image generation

To generate OG images for each page, I used astro-selfie Astro integration. It works by iterating over list of pages and rendering a page in a headless browser and taking screenshot of the top section of page using Playwright. Then that screenshot is used as an OG image for the page. One thing to note is that this process is slow, iterating over all pages and rendering them in a headless browser takes time, and with every change I was regenerating OG Images even though they were already created. So I “patch-package’d” astro-selfie to add a skip option, so I can skip generating OG images when needed.


UPDATE: The home page is slightly updated, previous version is here, and the new version is here.