<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.4">Jekyll</generator><link href="https://annafilou.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://annafilou.com/" rel="alternate" type="text/html" /><updated>2026-03-03T17:41:29+00:00</updated><id>https://annafilou.com/feed.xml</id><title type="html">Anna Filou</title><subtitle>Product/UI Designer based in Athens, working internationally.</subtitle><entry xml:lang="en"><title type="html">My work at Jest</title><link href="https://annafilou.com/en/jest" rel="alternate" type="text/html" title="My work at Jest" /><published>2026-02-26T00:00:00+00:00</published><updated>2026-02-26T00:00:00+00:00</updated><id>https://annafilou.com/en/jest</id><content type="html" xml:base="https://annafilou.com/en/jest"><![CDATA[<p><img src="/assets/jest/profiles-3.webp" alt="Jest.com profile screen on phone with fanned-out user profile cards." /></p>

<p><a href="https://about.jest.com/">Jest</a> is the first marketplace for messaging games—a real alternative to the app store model.</p>

<p>As the team’s Product Designer, I help shape how the platform looks, feels, and behaves. My work spans early concepting, product flows, UI design, and product management.</p>

<figure>
  <img src="/assets/jest/jest-techcrunch.webp" alt="Screenshot of TechCrunch article: Jest, a marketplace for messaging games, is challenging the app store status quo." />
  <figcaption><a href="https://techcrunch.com/2026/02/26/jest-a-marketplace-for-messaging-games-is-challenging-the-app-store-status-quo/">TechCrunch coverage</a>, February 2026.</figcaption>
</figure>

<ul>
  <li>We’re live in the US on <a href="https://jest.com/">Jest.com</a></li>
  <li>Learn more on our corporate website, <a href="https://about.jest.com/">about.jest.com</a></li>
  <li>If you’re a game developer or studio, check out the <a href="https://about.jest.com/fund/">Jest Games Fund</a> for up to $1M in support to publish your game on Jest!</li>
</ul>]]></content><author><name></name></author><category term="en" /><category term="case study" /><summary type="html"><![CDATA[]]></summary></entry><entry xml:lang="en"><title type="html">Smart Carousels with CSS</title><link href="https://annafilou.com/en/carousels" rel="alternate" type="text/html" title="Smart Carousels with CSS" /><published>2026-01-07T00:00:00+00:00</published><updated>2026-01-07T00:00:00+00:00</updated><id>https://annafilou.com/en/carousels</id><content type="html" xml:base="https://annafilou.com/en/carousels"><![CDATA[<p>You’ve undoubtedly seen many carousels on the web. You know how usually the content gets cut off?</p>

<p><img src="/assets/carousel-cutoff.webp" alt="" /></p>

<p>The issue arises because, when designing for the web, we need content to have a <strong>maximum width</strong>. Otherwise, if someone has a wide monitor, the layout looks broken or they have to move their head all the way from left to right to read it.</p>

<p>When we give containers a maximum width, anything that doesn’t fit inside them gets cut off and becomes invisible. In a carousel, that means as you scroll, the content disappears abruptly before the end of the window. (Unless the window is narrower than the max width you set. In other words, this isn’t a problem on mobile.)</p>

<p>Everything on a website is a box. The carousel is also a box. When you put something in the box, it appears on the leftmost side. If you want it to show up more to the right, you can add padding on the left. So hypothetically, we could add padding to align the first book with the left edge of the other sections! Alas… the padding would need to be different depending on the window’s width!</p>

<p>So for a long time, I thought this was an unavoidable problem. But then I saw <a href="https://www.apple.com/iphone/">a carousel that worked right on Apple’s website</a>! So… there was a way!</p>

<p>Thankfully, because this is the web, you can right-click → Inspect Source on any website to see exactly how it’s built. I then adapted it for my website, and ta-da!</p>

<figure>
<video autoplay="" loop="" muted="" src="/assets/carousel-demo.mp4" class="w-100 br4"></video>
<figcaption><a href="/reading">See it live</a></figcaption>
</figure>

<p>The code ↴</p>

<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$container</span><span class="p">:</span> <span class="m">1240px</span><span class="p">;</span> <span class="cm">/* Yes, I use Sass. Yes, I know we have CSS variables now, but I like it and the pre-processor is built into Jekyll, which builds my site anyway! */</span>
<span class="nv">$container-padding</span><span class="p">:</span> <span class="m">32px</span><span class="p">;</span>

<span class="nc">.book-carousel</span> <span class="p">{</span>
    <span class="nl">padding</span><span class="p">:</span> <span class="m">0</span> <span class="nf">calc</span><span class="p">(</span><span class="m">50%</span> <span class="o">-</span> <span class="nf">min</span><span class="p">(</span><span class="nv">$container</span><span class="o">,</span> <span class="m">100%</span><span class="p">)</span> <span class="o">/</span> <span class="m">2</span> <span class="o">+</span> <span class="nv">$container-padding</span><span class="p">);</span> <span class="cm">/* [Half the screen] - [half the container] + [container padding * 2] */</span>
    <span class="nl">overflow-x</span><span class="p">:</span> <span class="nb">auto</span><span class="p">;</span> <span class="cm">/* makes it horizontally scrollable */</span>
    <span class="nl">scroll-snap-type</span><span class="p">:</span> <span class="nb">x</span> <span class="nb">proximity</span><span class="p">;</span> <span class="cm">/* so you can’t stop in between books: it “snaps” to the closest element in the X axis */</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The magic part is:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code>calc(50% - min($container, 100%) / 2 + $container-padding);
</code></pre></div></div>

<p>We’re basically saying:</p>
<ul>
  <li>Take half of the screen.</li>
  <li>Subtract half of the max-width container from it OR half the screen (in which case the result is 0), whichever is smaller. This gives us the effective margin on each side.</li>
  <li>Add the container padding. Now we have <strong>the exact distance between the content of the other sections and the edge of the viewport</strong> on each side!</li>
</ul>

<p><img src="/assets/carousel-diagram.webp" alt="" /></p>

<p>And the coolest part is that this is all just CSS! Which means, if scripts don’t load (bad connection, on the train, unknown error, browser that blocks scripts), the carousel still works the same (progressive enhancement 😎).</p>

<hr />

<p>Did you notice the custom scrollbar? Don’t ask me how that one works because I asked Cursor to build it. You can <a href="https://github.com/anna-filou/anna-filou.github.io/blob/master/assets/js/book-carousel-scrollbar.js">see the code on GitHub</a> though.</p>]]></content><author><name></name></author><category term="en" /><category term="product" /><summary type="html"><![CDATA[You’ve undoubtedly seen many carousels on the web. You know how usually the content gets cut off?]]></summary></entry><entry xml:lang="en"><title type="html">How I build websites</title><link href="https://annafilou.com/en/how-websites" rel="alternate" type="text/html" title="How I build websites" /><published>2026-01-05T00:00:00+00:00</published><updated>2026-01-05T00:00:00+00:00</updated><id>https://annafilou.com/en/how-websites</id><content type="html" xml:base="https://annafilou.com/en/how-websites"><![CDATA[<p><img src="/assets/how-websites/hero.webp" alt="" /></p>

<p>I’ve been building websites for over 7 years, but until now I’d never shared my entire process online.</p>

<p>Everyone is talking about Framer and Webflow, and most build with WordPress. <strong>I do things differently</strong>. Not to be contrarian, but because I prefer having more freedom.</p>

<p>The post gets a bit technical at times, but <strong>if you don’t consider yourself technical, it’s still very much worth reading</strong> (regardless of whether you’re thinking about working with me). It’ll give you a practical understanding of how the web works.</p>

<hr />

<h2 id="table-of-contents">Table of contents</h2>
<ul>
  <li><a href="#my-approach">My approach</a></li>
  <li><a href="#going-live-hosting">Going live: Hosting</a></li>
  <li><a href="#making-sites-editable">Making sites editable</a></li>
  <li><a href="#templating-languages">Templating Languages</a></li>
  <li><a href="#getting-a-domain-name">Getting a domain name</a></li>
  <li><a href="#setting-up-custom-email">Setting up custom email</a></li>
</ul>

<hr />

<h2 id="my-approach">My approach</h2>

<p>I hand code websites using <a href="https://developer.mozilla.org/en-US/docs/Web/HTML">HTML</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS">CSS</a> and a bit of <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript">JavaScript</a>.</p>

<p>If you’re new to this, just know that HTML is a file type and websites are just folders with files that are linked to each other. You know how you open <code class="language-plaintext highlighter-rouge">.docx</code> files using Word? Well, you open <code class="language-plaintext highlighter-rouge">.html</code> files using a web browser, like Chrome!</p>

<p>I use <a href="https://jekyllrb.com/">Jekyll</a>, a Static Site Generator (SSG), to ‘sandwich’ together (“bundle”) parts of my code (e.g. navigation bar, footer, etc.) into a full website. Jekyll, like <a href="https://jamstack.org/generators/">other SSGs</a>, runs on my computer and I control it via terminal commands.</p>

<p><img src="/assets/how-websites/editor.webp" alt="" /></p>

<p>The reason I do that instead of just making one HTML file per webpage is that some elements repeat across pages (e.g. head, nav, footer) and some pages look exactly the same but have different content (think individual articles in a news site, or the same pages in different languages). I don’t wanna do duplicate work and risk creating inconsistencies. Even if a website has a single page at first, I might add more later. And even if I don’t, it’s often helpful to have separate files for different sections because it’s easier to find where to make changes when needed.</p>

<p>Jekyll creates a folder within my project folder called <code class="language-plaintext highlighter-rouge">_site</code> and in it it puts the sandwiched version. That version works completely independently and offline (as all sites do), unless I have built any dependencies into the code itself (for example, if I embed a YouTube video, it’s not gonna play offline).</p>

<h2 id="going-live-hosting">Going live: Hosting</h2>

<p>To put the website on the internet, I use <a href="https://netlify.app/">Netlify</a>, a <a href="https://en.wikipedia.org/wiki/Content_delivery_network">Content Delivery Network</a> (CDN). They offer free hosting, and since they’re a CDN, that means they put a copy of my website (<code class="language-plaintext highlighter-rouge">_site</code> folder) on many servers across the globe, and they serve from the one closest to the visitor for increased speed.</p>

<p><img src="/assets/how-websites/netlify.webp" alt="" /></p>

<p>Netlify can actually use Jekyll (among others) on their side to ‘sandwich’ (bundle) the code I upload. So I don’t have to upload the finished <code class="language-plaintext highlighter-rouge">_site</code> folder; instead I connect it to <a href="http://github.com/">GitHub</a> directly and Netlify rebuilds the site every time I push a change.</p>

<p>Note: Since Netlify builds the site independently from me, I might occasionally run into issues where the site gets built fine on my computer, but Netlify runs into errors. These are usually because of different <a href="https://www.ruby-lang.org/en/">Ruby</a> versions (the language powering Jekyll) running on Netlify’s servers vs locally on my machine. To resolve them I typically have to make changes to the “<a href="https://jekyllrb.com/docs/step-by-step/10-deployment/#gemfile">Gemfile</a>”, a text file that tells Jekyll which version of what to use.</p>

<h2 id="making-sites-editable">Making sites editable</h2>

<p><img src="/assets/how-websites/cms.webp" alt="" /></p>

<p>But how does a non-technical person (e.g. my client) edit their website if the content is stored in the code?</p>

<p>Well, Jekyll &amp; Co. let us “pull in” data from text files (<a href="https://en.wikipedia.org/wiki/Markdown">Markdown</a> and <a href="https://en.wikipedia.org/wiki/YAML">YML</a>, a more human-readable way to write <a href="https://en.wikipedia.org/wiki/JSON">JSON</a>) to put into our code. It’s still confusing for most people to edit YML files though, and more importantly, they might accidentally change the formatting (it takes just adding or removing a single space!) and break the whole thing. Thankfully there exist programs called <strong>Git-based Content Management Systems</strong> (CMS). They let people edit specific sections within files on GitHub through a simple interface, without letting them change parts we don’t explicitly allow.</p>

<p>I’ve successfully used <a href="https://pagescms.org/">Pages CMS</a> a bunch of times. It looks good and works well, but it’s missing many features. In the past I’ve used <a href="https://decapcms.org/">Decap CMS</a> back when it was <strong>Netlify CMS</strong>, but the interface was not as intuitive for editors.</p>

<p>To set up a Git-based CMS, you typically need to create a text file to tell the CMS what inputs (text, dropdown, etc.) to present to the user and what information these should alter in which file. (It’s a tedious process but ChatGPT can help.)</p>

<h3 id="alternative-cms-options">Alternative CMS options</h3>

<p>Git-based CMS is not the only option. There are also API-based CMSs, and they work by letting us store data (text, images…) on their servers, and then making that data available to us via an <a href="https://en.wikipedia.org/w/index.php?title=API">API</a>. Think of it like asking Jekyll to pull in data from a remote server, instead of a YML file in the same parent folder. We can pull in data each time I build the website so it’s part of the shipped code (good), or inject it into the live website each time somebody visits it (bad if it can be avoided, because we introduce latency and need JavaScript for it to work).</p>

<p>The upside of API-based CMS is that you can use the same data to power multiple “clients” (e.g. a website, an iOS App, an Android app, a POS…). The downside is that your data is not yours; if the CMS company goes away, so does the data. Or they might increase their prices and you’ll be almost forced to start paying them more, because changing an older project to use a different CMS is often cumbersome.</p>

<p>And a third option is traditional “monolithic” CMS, like <a href="https://wordpress.com/">WordPress.com</a>. Monolithic because they take care of both the website generation and the content management (think of it like Jekyll + Pages CMS into one). All-in-one might sound practical, but it forces you to use certain patterns you might not like or need for a project because you can’t cherry pick the parts you like—it’s all or nothing.</p>

<p>For example, let’s say I like Wordpress’ content editing part because it’s easy for clients to use (not really—it’s terrible, but let’s pretend) but I don’t like writing <a href="https://en.wikipedia.org/wiki/PHP">PHP</a> to create my page templates, nor do I want to rent server space and install and manage Wordpress updates. If I want to use Wordpress, I have to do all of these together. But with “decoupled” CMS (whether Git- or API-based), I can switch each part individually. I might be using PagesCMS and then switch to Decap by changing a single text file (configuration) without having to give up Jekyll. Or the opposite! I might switch from Jekyll to, say, <a href="https://gohugo.io/">Hugo</a>, without giving up PagesCMS.</p>

<h2 id="templating-languages">Templating Languages</h2>

<p>This decoupling can go even further, as many SSGs let you choose which templating language you want to use. Jekyll uses <a href="https://shopify.github.io/liquid/">Liquid</a>, but other SSGs, like <a href="https://www.11ty.dev/">Eleventy</a>, let you choose between many, including Liquid, <a href="https://handlebarsjs.com/">Handlebars</a>, <a href="https://mustache.github.io/">Mustache</a>, and more. Templating languages generally all work the same way. They have an opening and closing symbol combination (often <code class="language-plaintext highlighter-rouge">{{</code> and <code class="language-plaintext highlighter-rouge">}}</code>, hence names like “Mustache”) that are not natively part of HTML. That way, the templating language can recognize its parts and replace them with the thing you specify. For example, I might use Liquid inside HTML to write:</p>

<div class="language-liquid highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;a href="<span class="cp">{{</span><span class="w"> </span><span class="nv">site</span><span class="p">.</span><span class="nv">data</span><span class="p">.</span><span class="nv">links</span><span class="p">.</span><span class="nv">search</span><span class="p">.</span><span class="nv">url</span><span class="w"> </span><span class="cp">}}</span>"&gt;<span class="cp">{{</span><span class="w"> </span><span class="nv">site</span><span class="p">.</span><span class="nv">data</span><span class="p">.</span><span class="nv">links</span><span class="p">.</span><span class="nv">search</span><span class="p">.</span><span class="nv">title</span><span class="w"> </span><span class="cp">}}</span>&lt;/a&gt;
</code></pre></div></div>

<p>The part inside the brackets is Liquid, and the rest is HTML. Assuming I’m using Jekyll to build the website, for the above to work, there needs to be a file called <code class="language-plaintext highlighter-rouge">links.yml</code> inside a folder named <code class="language-plaintext highlighter-rouge">_data</code> inside the parent folder that includes all the website’s files (<code class="language-plaintext highlighter-rouge">site</code>). Inside that <code class="language-plaintext highlighter-rouge">links.yml</code> I need to have</p>

<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">links</span><span class="pi">:</span>
<span class="err">	</span><span class="pi">-</span> <span class="s">search</span>
<span class="na">		title</span><span class="pi">:</span> <span class="s">Google</span>
<span class="na">		url</span><span class="pi">:</span> <span class="s">https://google.com</span>
</code></pre></div></div>

<p>Then Jekyll will know to replace the Liquid snippets above and produce:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"https://google.com"</span><span class="nt">&gt;</span>Google<span class="nt">&lt;/a&gt;</span>
</code></pre></div></div>

<p>And that will be the code in the outputted HTML inside my <code class="language-plaintext highlighter-rouge">_site</code> folder.</p>

<h2 id="getting-a-domain-name">Getting a domain name</h2>

<p><img src="/assets/how-websites/domains.webp" alt="" /></p>

<p>All that is great, but how does someone visit my website on the internet? I want them to type something like <code class="language-plaintext highlighter-rouge">example.com</code> into their browser and see my page. To do that, I need to buy the domain name “example.com”. I buy domains from companies known as domain registrars. <a href="https://iwantmyname.com/">iwantmyname.com</a> is one such company and while I appreciated their no-nonsense minimalist approach for years, their prices have gone up significantly lately. So I switched to buying from <a href="https://porkbun.com/">Porkbun</a>!</p>

<p>After I buy a domain, I need to point it to where my website is stored (‘hosted’). I do that by adding a <a href="https://en.wikipedia.org/w/index.php?title=Domain_Name_System">DNS</a> record in the domain registrar’s software platform. What does the record say? Depends on where the site is hosted. In my case, that’s usually Netlify, so Netlify gives me some IP addresses to copy-paste and they take care of the rest.</p>

<p><img src="/assets/how-websites/dns.webp" alt="" /></p>

<h2 id="setting-up-custom-email">Setting up custom email</h2>

<p>What if I want to receive email in an address like <code class="language-plaintext highlighter-rouge">me@example.com</code>?</p>

<p>First, I need an email service provider. Gmail is such a provider, but to use it with a custom domain, you need to buy a “Google Workspace” subscription. Most email services ask you to pay to use a custom domain email.</p>

<p>The only reliable free solution that I know of is <a href="https://www.zoho.com/mail/">Zoho Mail</a>, but that too has a catch: when using the free version of the service, you can only view your emails from their website or their mobile app. Good enough, considering it’s free! But for just 12 € per <strong>year</strong> (not per month), you can use any email client you like (for example Apple Mail, Outlook, or even Gmail—yes, Gmail can be used as an email client for other services!) via the protocols <a href="https://en.wikipedia.org/wiki/Internet_Message_Access_Protocol">IMAP</a> (receiving email) &amp; <a href="https://en.wikipedia.org/w/index.php?title=Simple_Mail_Transfer_Protocol">SMTP</a> (sending email) or <a href="https://en.wikipedia.org/wiki/Post_Office_Protocol">POP3</a> (both).</p>

<p><img src="/assets/how-websites/email.webp" alt="" /></p>

<hr />

<p>And now you know how I make websites! Of course the process is subject to change. I love trying out all the latest tools, but I’ve been loyal to Jekyll for so long because it just… works.</p>

<p>The Git-based CMS landscape on the other hand changes constantly, so every few years I need to switch to something else. It’s not a huge deal, because the data always remains accessible and editable (remember the YML files?), since it’s decoupled from the CMS.</p>

<p>And that’s the whole point of this approach: you’re never locked in. If a tool stops working for you, you can swap it out without losing your content or starting from scratch. And that flexibility benefits everyone: clients get sites that will last, and collaborators get a flexible stack that’s easy to work with!</p>

<p><strong>Questions? Suggestions?</strong> Reach out—I’m all ears!</p>]]></content><author><name></name></author><category term="en" /><category term="product" /><summary type="html"><![CDATA[]]></summary></entry><entry xml:lang="en"><title type="html">Why I built Today Todo</title><link href="https://annafilou.com/en/building-today" rel="alternate" type="text/html" title="Why I built Today Todo" /><published>2025-10-12T00:00:00+00:00</published><updated>2025-10-12T00:00:00+00:00</updated><id>https://annafilou.com/en/building-today</id><content type="html" xml:base="https://annafilou.com/en/building-today"><![CDATA[<p><img src="/assets/today-todo-4x3.webp" alt="3 mockups showing a minimal todo app in dark mode. The third screen is of interest: it shows a modal with the title “it’s a new day”, a list of todos, and a button that reads “Clear and Start Today”" /></p>

<p>Like many tech workers, I have a complicated relationship with to-do apps. On one hand, I’ve spent countless hours trying them all—and even making my own. On the other, they just. Don’t. Work.</p>

<p>I’ve literally tried them all: Todoist, Microsoft To Do, Sunsama, Trello, Superlist, Taskade… and I’ve built at least two fully custom, complicated task management systems in Coda, with features like automatic prioritization based on parameters and warnings when there aren’t enough hours in the day to finish everything I’ve scheduled.</p>

<p>They all meet the same fate. It doesn’t matter how much I like them at first, how motivated I feel, or how productive I get—eventually I’ll miss a day or two. Then, when I come back, I’m greeted by an overwhelming list of overdue tasks—exactly what I was trying to <strong>avoid</strong> in the first place.</p>

<figure>
  <img src="/assets/todo-archive-microsoft.webp" alt="Screenshot of a bloated task list" />
  <figcaption>Many of the tasks I add to a task manager are by nature <strong>ephemeral</strong>, like “call hotel for reservation” or “tidy up clothes drawer.” I’ve long since completed them (or not), and yet they’ve been sitting there for years. Is that helping me?</figcaption>
</figure>

<h2 id="second-brain-anyone-">Second brain, anyone? 🧠</h2>

<p>I’ve seen the second-brain systems. They make so much sense: capture every idea, throw it into your inbox for later review, spend five minutes at the end of the day deciding what goes where, and once a month review everything to prioritize what matters. Human memory is fragile, but the app never forgets.</p>

<p>Sounds great!</p>

<p>Except I never look at my inbox. I never <strong>feel</strong> like looking at my inbox—the thought alone overwhelms me. It’s exciting for a few days (“think how much my future self will thank me!” Not…), but the motivation always fades. Before long, I’ve left behind another digital graveyard of things I once thought I should do.</p>

<p>The promise of these apps is that they’ll let us capture everything so we can rest easy knowing we won’t forget. But that’s the problem: <strong>they never let us forget!</strong> The constant reminder of unfinished tasks clutters my brain.</p>

<blockquote>
  <p>Forgetting is a feature of memory, not a bug. You let the unimportant stuff fade so you can focus.</p>
</blockquote>

<p>I know, you don’t <strong>have</strong> to see all your tasks at once. You can focus on what’s due today while everything else sits in the backlog. But I <strong>know</strong> it’s there, and it stresses me out. What if I missed a few things two days ago, and then another yesterday? I open the app and see a growing list of overdue tasks. Some people can calmly sort through them—decide what’s still important, move things around, delete the rest.</p>

<p>I’m not one of those people. I’m a hoarder. I have a hard time throwing things away—or removing apps from my phone—and that extends to tasks. What if I need them again someday?</p>

<h2 id="what-works">What works</h2>

<p>You know what’s actually worked for me, for years?</p>

<p>A paper notebook. <sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup></p>

<p><img src="/assets/todo-notebook.webp" alt="Notebook spread with sketches on the left page and a tight list of tasks with checkboxes of various states on the right" /></p>

<p>I write tasks down as they come. When I finish one, I check it off. A single page usually lasts me a few days or weeks. When it fills up, I turn the page and I’m forced to make decisions: <strong>which uncompleted tasks do I bring over?</strong></p>

<p>That <strong>friction</strong> of having to manually re-write incomplete tasks forces me to ask whether they’re actually worth doing. And that’s what to-do apps have been missing!</p>

<p>On paper, the default is that incomplete tasks “disappear” when you turn the page. In digital apps, the default is that they become “overdue” and haunt you. Even if they get hidden in a backlog, I still know they’re there—and that knowledge alone creates <strong>guilt</strong>. I end up browsing through old tasks, resurrecting ultimately useless ones just because I feel bad for not having done them.</p>

<h2 id="the-digital-equivalent">The digital equivalent?</h2>

<p>I built my own to-do app where, at the end of the day, unfinished tasks just… <strong>vanish</strong>. They go away! Better yet, they don’t <strong>just</strong> vanish. When you open the app in the morning, it shows you the list of tasks you didn’t finish yesterday. There’s only one possible action: <strong>delete the list</strong>! You can’t roll tasks over to the next day.</p>

<p>If you see something important—something you actually want to do—you have to remember it for a few seconds and re-write it yourself. How many unimportant tasks survive that process? Not many, maybe none! And that’s the point: no backlog, no guilt.</p>

<p><img src="/assets/today-todo-modals.webp" alt="Two screens showing two different modals: One says &quot;Welcome&quot; and explains how the app works. The other shows it's a new day. Here are the tasks you didn't finish yesterday, are they still important? If yes, add them back from memory  Beneath the list of tasks, there is a button that reads &quot;Clear&quot; and &quot;Start Today&quot;" /></p>

<h2 id="try-it">Try it!</h2>

<p>It’s surprisingly freeing. You should try it—and you can! <a href="https://today.annafilou.com/">Right here</a>.</p>

<p>There’s a link in the settings to email me feedback if you’d like.</p>

<p>Note: it’s a Progressive Web App, not just a website. You can “install” it and use it offline! <a href="https://www.cdc.gov/niosh/mining/tools/installpwa.html">Here’s how</a>.</p>

<hr />

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>I recently switched to an E-Ink writing tablet—the Onyx Boox Go 10.3”. Highly recommended if you’re into that sort of thing. <img src="/assets/onyx-cafe.webp" alt="A tablet with an E-Paper screen on a table in a cafe. Latte visible in the background. Hand visible holding a Wacom pen." /> <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="en" /><category term="product" /><summary type="html"><![CDATA[]]></summary></entry><entry xml:lang="en"><title type="html">Database Design System</title><link href="https://annafilou.com/en/database" rel="alternate" type="text/html" title="Database Design System" /><published>2025-10-05T00:00:00+00:00</published><updated>2025-10-05T00:00:00+00:00</updated><id>https://annafilou.com/en/database</id><content type="html" xml:base="https://annafilou.com/en/database"><![CDATA[<p><a href="https://www.instagram.com/thedatabase.co/">Database™</a> has already established itself as a trusted resource for digital designers. When <a href="https://www.haritos.co/">Constantinos Haritos</a>, the Brand Designer who created it, asked me for some help with the Design System for the upcoming web platform, I was super excited to contribute.</p>

<p>The design of the platform was mostly complete, and I turned the designs into reusable components and responsive styles. I also created some tricky internal pages and flows using the newly established design patterns.</p>

<figure>
  <img src="/assets/database/submission-form.webp" alt="" />
  <figcaption>
  One of the steps of the project submission form that I designed.
  </figcaption>
</figure>

<figure>
  <img src="/assets/database/type-variables.webp" alt="" />
  <figcaption>
  I merged the separate desktop and mobile styles into single styles bound to responsive variables.
  </figcaption>
</figure>

<figure>
  <img src="/assets/database/login-component.webp" alt="" />
  <figcaption>
  Responsive components for modals and forms with desktop and mobile variants.
  </figcaption>
</figure>

<figure>
  <img src="/assets/database/text-input.webp" alt="" />
  <figcaption>
  Input fields with active and error states.
  </figcaption>
</figure>

<figure>
  <img src="/assets/database/media-card.webp" alt="" />
  <figcaption>
  The various states of the media upload card, including hover states.
  </figcaption>
</figure>]]></content><author><name></name></author><category term="en" /><category term="case study" /><summary type="html"><![CDATA[Database™ has already established itself as a trusted resource for digital designers. When Constantinos Haritos, the Brand Designer who created it, asked me for some help with the Design System for the upcoming web platform, I was super excited to contribute.]]></summary></entry><entry xml:lang="en"><title type="html">My work at Shopflix</title><link href="https://annafilou.com/en/shopflix" rel="alternate" type="text/html" title="My work at Shopflix" /><published>2025-05-26T00:00:00+00:00</published><updated>2025-05-26T00:00:00+00:00</updated><id>https://annafilou.com/en/shopflix</id><content type="html" xml:base="https://annafilou.com/en/shopflix"><![CDATA[<p>From April 2023 until October 2025 I was working as a UI / Product Designer at <a href="https://shopflix.gr/">Shopflix</a>, a major e-commerce marketplace in Greece.</p>

<p>I was initially brought on for UI Design, but over time my role evolved into what we now call Product Design, spanning everything from visual design to component systems and behavior analytics.</p>

<p>I can’t share details due to the NDA, but here’s the gist of my 2.5 years at Shopflix:</p>

<h3 id="year-1">Year 1</h3>
<p>I spent most of my time patching up existing designs as they were being implemented, and rushing to design new features using problematic components in the name of speed and consistency. Alongside that I was creating ad-hoc marketing assets while building a component library in Figma and laying the foundations of a design system on the side.</p>

<p>Due to the replatforming taking place at the same time, rethinking flows was off the table. I hated adapting work I believed was poorly thought-out, but I developed something unique:</p>

<p><strong>I turned complete pages into components</strong> (with variables) <strong>that I could drop into any flow, ensuring all user journeys would always display the latest version</strong>. This solved a major issue: developers would often start working on a feature many months after I’d designed it, and by then there were updates to pages within that flow. Before this approach, they kept accidentally rolling back each other’s work because each person was looking at a different version!</p>

<p>Here’s the component system in action:</p>

<figure>
  <video autoplay="" loop="" muted="" src="/assets/shopflix/shopflix-checkout-cart-components.mp4" class="w-100 br3"></video>
  <figcaption>Screen recording showing real-time interaction with checkout and cart component instances, toggling variants like device type (desktop/mobile) with instant page updates.</figcaption>
</figure>

<h3 id="year-2">Year 2</h3>
<p>With a major deadline gone, I finally had the freedom to explore and pitch improvements. But most of my usability-focused proposals initially got shelved.</p>

<p>That phase taught me as much about organizational dynamics as it did about design at scale. I gradually built stakeholder buy-in by consistently shipping good work, sharing honest opinions, and pushing back when needed. I also got involved with anything I could help improve, even outside my defined role.</p>

<h3 id="year-3">Year 3</h3>
<p>My persistence paid off because on Year 3 an organizational shift allowed me to work on the kind of projects I wanted to from the beginning: remaking core flows from scratch.</p>

<p>As of writing this, the bulk of my design work has not been implemented yet (so I can’t show it here 😮‍💨), but it includes major updates for the:</p>
<ul>
  <li>Checkout Flow</li>
  <li>Product Page</li>
  <li>Homepage</li>
  <li>Orders</li>
  <li>Cart</li>
</ul>

<p>We’d long been aware of the shortcomings, but conflicting priorities did not previously allow the time to address them. I can’t describe the satisfaction I felt seeing the enormous usability improvements and it goes without saying that I can’t wait to see them live!</p>

<p>I left Shopflix knowing it’s on a much better trajectory than when I joined, and I’m confident that the team will continue doing great work to bring the platform to the state we know it can reach!</p>]]></content><author><name></name></author><category term="en" /><category term="case study" /><summary type="html"><![CDATA[From April 2023 until October 2025 I was working as a UI / Product Designer at Shopflix, a major e-commerce marketplace in Greece.]]></summary></entry><entry xml:lang="en"><title type="html">How I generate color scales in CSS with OKLCH</title><link href="https://annafilou.com/en/css-color-scales" rel="alternate" type="text/html" title="How I generate color scales in CSS with OKLCH" /><published>2025-05-13T00:00:00+00:00</published><updated>2025-05-13T00:00:00+00:00</updated><id>https://annafilou.com/en/css-color-scales</id><content type="html" xml:base="https://annafilou.com/en/css-color-scales"><![CDATA[<p><img src="/assets/anna-color-scale-css.webp" alt="" /></p>

<h2 id="the-idea">The idea</h2>

<p>I wanted to create a single variable in my CSS, e.g. <code class="language-plaintext highlighter-rouge">--brand: #ff6800;</code>, and have an entire color scale (with variables like <code class="language-plaintext highlighter-rouge">--brand-10: #ffe4d3; --brand-20: #ffd2b2;</code> and so on) automatically “generated” for me. Like this! ⤵︎</p>

<p><img src="/assets/color-scale-example.webp" alt="" /></p>

<p>Similar to what you get from sites like <a href="https://uicolors.app/generate">uicolors.app’s Tailwind CSS Color Generator</a>, but locally, in your CSS file.</p>

<p>Why? When playing around with a new design, not yet sure which colors to go with, being able to tweak a couple of values directly in my CSS and preview my own design in different colors would be super practical.</p>

<p>Now, you <em>might</em> be wondering:</p>

<h2 id="what-is-oklch">What is OKLCH?</h2>

<p>These people have done a much better job explaining than I could at the moment:</p>

<ul>
  <li>
    <p><a href="https://evilmartians.com/chronicles/oklch-a-color-picker-made-to-help-think-perceptively">OK, OKLCH: a color picker made to help think perceptively, by Evil Martians</a></p>

    <p>“OKLCH is <strong>a new way of encoding colors</strong> (like HEX, RGBA, or HSL). It always has predictable lightness after color transformations (compared to HSL), it’s capable of encoding a wider range of colors, it offers native browser support, and unlike LCH and Lab, <strong>it has no hue shift</strong> when chroma is changed.”</p>
  </li>
  <li>
    <p><a href="https://keithjgrant.com/posts/2023/04/its-time-to-learn-oklch-color/">It’s Time to Learn oklch Color, by Keith J. Grant</a></p>

    <p>“In HSL, 100% saturation is simply as saturated as that particular color can be in the sRGB gamut. In OKLCH, the values aren’t based on technical limits or a mathematical definition, but rather on perceived equality. <strong>The amount of lightness indicates exactly how bright the color is</strong>, and the amount of chroma indicates exactly how vivid it is. The human eye perceives some colors like green or yellow to be brighter than others, like blue or purple, and OKLCH takes these details into account.”</p>
  </li>
</ul>

<p>The gist of it is that, similar to HSL, you can keep the Hue constant while adjusting the Lightness and Saturation (Chroma, in this case) independently. What sets OKLCH apart is that, unlike HSL and the others, all colors with the same hue <strong>actually look like they have the same hue</strong>! You know how making a vibrant blue lighter makes it look purple? Well, in OKLCH it still looks blue!</p>

<figure>
  <img src="/assets/blue-hue-difference-atmos.webp" alt="Difference between a blue color scale in LCH vs OKLCH" />
  <figcaption>Image from <a href="https://atmos.style/blog/lch-vs-oklch">atmos.style</a></figcaption>
</figure>

<p>You can also use <code class="language-plaintext highlighter-rouge">oklch(from…)</code> to convert HEX, RGB, etc. colors into OKLCH and <em>then</em> mess with their Lightness, Chroma, and Hue.</p>

<h2 id="what-i-did">What I did</h2>

<p>Firstly, I used the numbering format from the Tailwind color scales for familiarity.
Then I tried using <code class="language-plaintext highlighter-rouge">oklch(from…)</code> and adjusting the Alpha value to create a scale. But then colors on the scale could not be darker than the original. And if they ever overlap (think, text color with 80% opacity + background at 30%) then the color on top will be a mix of the two!</p>

<p>So I switched to using <code class="language-plaintext highlighter-rouge">calc()</code> instead of L and C values within <code class="language-plaintext highlighter-rouge">oklch()</code> and keeping the hue value the same.
I first created a color scale using an online generator, then converted to OKLCH and used a spreadsheet to calculate the difference between the Lightness and Chroma values of each color. I rounded those up and created variables in my CSS to represent the Lightness increment and the Chroma increment separately.</p>

<p><img src="/assets/color-scale-excel.webp" alt="" /></p>

<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">:root</span> <span class="p">{</span>

    <span class="cm">/* Base color */</span>
   <span class="na">--brand</span><span class="p">:</span> <span class="mh">#0d1bbd</span><span class="p">;</span>

    <span class="cm">/* Lightness and Chroma increments */</span>
    <span class="na">--l-increment</span><span class="p">:</span> <span class="m">0</span><span class="mi">.075</span><span class="p">;</span> <span class="cm">/* UP: Lighter*/</span>
    <span class="na">--c-increment</span><span class="p">:</span> <span class="m">0</span><span class="mi">.028</span><span class="p">;</span> <span class="cm">/* UP: LESS saturated*/</span>

    <span class="cm">/* Some of the resulting color tokens */</span>
    <span class="na">--bg-brand-50</span><span class="p">:</span> <span class="nf">oklch</span><span class="p">(</span><span class="n">from</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">brand</span><span class="p">)</span> <span class="nf">calc</span><span class="p">(</span><span class="n">l</span> <span class="o">+</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">l-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">8</span><span class="p">))</span> <span class="nf">calc</span><span class="p">(</span><span class="n">c</span> <span class="o">-</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">c-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">8</span><span class="p">))</span> <span class="n">h</span> <span class="o">/</span> <span class="m">1</span><span class="p">);</span>
    <span class="cm">/* … */</span>
    <span class="na">--bg-brand-800</span><span class="p">:</span> <span class="nf">oklch</span><span class="p">(</span><span class="n">from</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">brand</span><span class="p">)</span> <span class="n">l</span> <span class="n">c</span> <span class="n">h</span> <span class="o">/</span> <span class="m">1</span><span class="p">);</span> <span class="cm">/* This is the base color */</span>
    <span class="cm">/* … */</span>
    <span class="na">--bg-brand-950</span><span class="p">:</span> <span class="nf">oklch</span><span class="p">(</span><span class="n">from</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">brand</span><span class="p">)</span> <span class="nf">calc</span><span class="p">(</span><span class="n">l</span> <span class="o">-</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">l-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">2</span><span class="p">))</span> <span class="nf">calc</span><span class="p">(</span><span class="n">c</span> <span class="o">-</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">c-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">2</span><span class="p">))</span> <span class="n">h</span> <span class="o">/</span> <span class="m">1</span><span class="p">);</span>

<span class="p">}</span>
</code></pre></div></div>

<p>That seemed to work exactly how I imagined!</p>

<p>For that specific color…</p>

<h2 id="obstacles">Obstacles</h2>

<p>Obvious in hindsight: that method only works if the base colors all have similar Lightness and Chroma. There’s no way to create conditional statements (“if this, then that”) in CSS, so a base color would always have to occupy the same step on the color scale.</p>

<p>That can be sorted by spending a bit of extra time choosing base colors. Problem solved? Not so fast! Because of the way OKLCH works, some HEX/RGB/HSL colors do not have an OKLCH equivalent!</p>

<figure> 
    <img src="/assets/hsl-vs-oklch.webp" alt="Difference between a blue color scale in LCH vs OKLCH" /> 
    <figcaption>
        Image from <a href="https://evilmartians.com/chronicles/oklch-a-color-picker-made-to-help-think-perceptively">evilmartians.com</a>
    </figcaption> 
</figure>

<p><code class="language-plaintext highlighter-rouge">oklch(from…)</code> works by converting, say, a HEX value into OKLCH. But that means if you’re using HEX for your base variables, some colors are bound to look less saturated than the rest even if you max out their saturation in the HEX color space.</p>

<p><img src="/assets/color-scale-saturation.webp" alt="" /></p>

<h2 id="solution">Solution</h2>

<p>Switch to OKLCH for the base color variables. Then you can make sure the lightness and chroma of each color are the same, or close enough. Now, you do miss out on a couple of things when you do that…</p>

<ul>
  <li>You can’t just enter any color you like and get a color scale. You need to find its OKLCH Hue and input that instead. The original color might not even be part of the resulting color scale.</li>
  <li>Design tools like Figma do not support OKLCH, so you have to figure out the hue by other means.</li>
  <li>Code editors like VS Code don’t natively preview OKLCH colors like they do HEX, RGB, etc. You need to install an extension if you want that, but it can really slow down the app.</li>
</ul>

<p>But hey—it works! :D And changing a single number to get an entirely new color scale is kinda fun, like a design nerd’s slot machine. I figured this technique might be useful (and/or fun) to someone, so why not share.</p>

<p>Here’s the CSS you can use:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">:root</span> <span class="p">{</span>

    <span class="c">/* Base colors */</span>
    <span class="c">/* Change the HUE (last number) for different colors. */</span>
    <span class="c">/* You might wanna change the Lightness and Chroma slightly too in some cases. */</span>
    <span class="py">--brand</span><span class="p">:</span> <span class="nf">oklch</span><span class="p">(</span><span class="m">0.40</span> <span class="m">0.30</span> <span class="m">265.71</span><span class="p">);</span>

    <span class="c">/* add more variables here */</span>

    <span class="c">/* Adjuts scales */</span>
    <span class="py">--l-increment</span><span class="p">:</span> <span class="m">0.075</span><span class="p">;</span> <span class="c">/* UP: Lighter*/</span>
    <span class="py">--c-increment</span><span class="p">:</span> <span class="m">0.028</span><span class="p">;</span> <span class="c">/* UP: LESS saturated*/</span>

    <span class="c">/* Color tokens */</span>
    <span class="c">/* Use them in classes like this: var(----bg-brand-600) */</span>
    <span class="py">--bg-brand-50</span><span class="p">:</span> <span class="nf">oklch</span><span class="p">(</span><span class="n">from</span> <span class="nf">var</span><span class="p">(</span><span class="l">--brand</span><span class="p">)</span> <span class="nf">calc</span><span class="p">(</span><span class="n">l</span> <span class="o">+</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--l-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">8</span><span class="p">))</span> <span class="nf">calc</span><span class="p">(</span><span class="n">c</span> <span class="o">-</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--c-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">8</span><span class="p">))</span> <span class="n">h</span> <span class="o">/</span> <span class="m">1</span><span class="p">);</span>
    <span class="py">--bg-brand-100</span><span class="p">:</span> <span class="nf">oklch</span><span class="p">(</span><span class="n">from</span> <span class="nf">var</span><span class="p">(</span><span class="l">--brand</span><span class="p">)</span> <span class="nf">calc</span><span class="p">(</span><span class="n">l</span> <span class="o">+</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--l-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">7</span><span class="p">))</span> <span class="nf">calc</span><span class="p">(</span><span class="n">c</span> <span class="o">-</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--c-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">7</span><span class="p">))</span> <span class="n">h</span> <span class="o">/</span> <span class="m">1</span><span class="p">);</span>
    <span class="py">--bg-brand-200</span><span class="p">:</span> <span class="nf">oklch</span><span class="p">(</span><span class="n">from</span> <span class="nf">var</span><span class="p">(</span><span class="l">--brand</span><span class="p">)</span> <span class="nf">calc</span><span class="p">(</span><span class="n">l</span> <span class="o">+</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--l-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">6</span><span class="p">))</span> <span class="nf">calc</span><span class="p">(</span><span class="n">c</span> <span class="o">-</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--c-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">6</span><span class="p">))</span> <span class="n">h</span> <span class="o">/</span> <span class="m">1</span><span class="p">);</span>
    <span class="py">--bg-brand-300</span><span class="p">:</span> <span class="nf">oklch</span><span class="p">(</span><span class="n">from</span> <span class="nf">var</span><span class="p">(</span><span class="l">--brand</span><span class="p">)</span> <span class="nf">calc</span><span class="p">(</span><span class="n">l</span> <span class="o">+</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--l-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">5</span><span class="p">))</span> <span class="nf">calc</span><span class="p">(</span><span class="n">c</span> <span class="o">-</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--c-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">5</span><span class="p">))</span> <span class="n">h</span> <span class="o">/</span> <span class="m">1</span><span class="p">);</span>
    <span class="py">--bg-brand-400</span><span class="p">:</span> <span class="nf">oklch</span><span class="p">(</span><span class="n">from</span> <span class="nf">var</span><span class="p">(</span><span class="l">--brand</span><span class="p">)</span> <span class="nf">calc</span><span class="p">(</span><span class="n">l</span> <span class="o">+</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--l-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">4</span><span class="p">))</span> <span class="nf">calc</span><span class="p">(</span><span class="n">c</span> <span class="o">-</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--c-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">4</span><span class="p">))</span> <span class="n">h</span> <span class="o">/</span> <span class="m">1</span><span class="p">);</span>
    <span class="py">--bg-brand-500</span><span class="p">:</span> <span class="nf">oklch</span><span class="p">(</span><span class="n">from</span> <span class="nf">var</span><span class="p">(</span><span class="l">--brand</span><span class="p">)</span> <span class="nf">calc</span><span class="p">(</span><span class="n">l</span> <span class="o">+</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--l-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">3</span><span class="p">))</span> <span class="nf">calc</span><span class="p">(</span><span class="n">c</span> <span class="o">-</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--c-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">3</span><span class="p">))</span> <span class="n">h</span> <span class="o">/</span> <span class="m">1</span><span class="p">);</span>
    <span class="py">--bg-brand-600</span><span class="p">:</span> <span class="nf">oklch</span><span class="p">(</span><span class="n">from</span> <span class="nf">var</span><span class="p">(</span><span class="l">--brand</span><span class="p">)</span> <span class="nf">calc</span><span class="p">(</span><span class="n">l</span> <span class="o">+</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--l-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">2</span><span class="p">))</span> <span class="nf">calc</span><span class="p">(</span><span class="n">c</span> <span class="o">-</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--c-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">2</span><span class="p">))</span> <span class="n">h</span> <span class="o">/</span> <span class="m">1</span><span class="p">);</span>
    <span class="py">--bg-brand-700</span><span class="p">:</span> <span class="nf">oklch</span><span class="p">(</span><span class="n">from</span> <span class="nf">var</span><span class="p">(</span><span class="l">--brand</span><span class="p">)</span> <span class="nf">calc</span><span class="p">(</span><span class="n">l</span> <span class="o">+</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--l-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">1</span><span class="p">))</span> <span class="nf">calc</span><span class="p">(</span><span class="n">c</span> <span class="o">-</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--c-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">1</span><span class="p">))</span> <span class="n">h</span> <span class="o">/</span> <span class="m">1</span><span class="p">);</span>
    <span class="py">--bg-brand-800</span><span class="p">:</span> <span class="nf">oklch</span><span class="p">(</span><span class="n">from</span> <span class="nf">var</span><span class="p">(</span><span class="l">--brand</span><span class="p">)</span> <span class="n">l</span> <span class="n">c</span> <span class="n">h</span> <span class="o">/</span> <span class="m">1</span><span class="p">);</span>
    <span class="py">--bg-brand-900</span><span class="p">:</span> <span class="nf">oklch</span><span class="p">(</span><span class="n">from</span> <span class="nf">var</span><span class="p">(</span><span class="l">--brand</span><span class="p">)</span> <span class="nf">calc</span><span class="p">(</span><span class="n">l</span> <span class="o">-</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--l-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">1</span><span class="p">))</span> <span class="nf">calc</span><span class="p">(</span><span class="n">c</span> <span class="o">-</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--c-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">1</span><span class="p">))</span> <span class="n">h</span> <span class="o">/</span> <span class="m">1</span><span class="p">);</span>
    <span class="py">--bg-brand-950</span><span class="p">:</span> <span class="nf">oklch</span><span class="p">(</span><span class="n">from</span> <span class="nf">var</span><span class="p">(</span><span class="l">--brand</span><span class="p">)</span> <span class="nf">calc</span><span class="p">(</span><span class="n">l</span> <span class="o">-</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--l-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">2</span><span class="p">))</span> <span class="nf">calc</span><span class="p">(</span><span class="n">c</span> <span class="o">-</span> <span class="nf">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--c-increment</span><span class="p">)</span> <span class="o">*</span> <span class="m">2</span><span class="p">))</span> <span class="n">h</span> <span class="o">/</span> <span class="m">1</span><span class="p">);</span>

<span class="p">}</span>
</code></pre></div></div>

<hr />

<h2 id="disclosure">Disclosure</h2>
<p>I originally planned to name this post “What I learned making an OKLCH color scale generator in CSS” and it was gonna have a section titled “Why it failed”. While writing that I realized… I had not failed. I just needed to tweak some Chroma values (and mention some caveats)! ;)</p>

<h3 id="special-thanks">Special thanks…</h3>
<p>…to <a href="https://github.com/dragos-efy/efy">Dragos</a> for nagging me about how cool OKLCH is for months, until I checked it out. 😁</p>]]></content><author><name></name></author><category term="en" /><category term="code" /><summary type="html"><![CDATA[]]></summary></entry><entry xml:lang="el"><title type="html">Γιατί δεν φτιάχνω φθηνά site</title><link href="https://annafilou.com/el/mle" rel="alternate" type="text/html" title="Γιατί δεν φτιάχνω φθηνά site" /><published>2025-05-13T00:00:00+00:00</published><updated>2025-05-13T00:00:00+00:00</updated><id>https://annafilou.com/el/el-mle</id><content type="html" xml:base="https://annafilou.com/el/mle"><![CDATA[<p>Πελάτης μου στον χώρο μεταφορών είχε ήδη site. Αρχική σελίδα με καρουζέλ γεμάτο stock φωτογραφίες (συμπεριλαμβανομένης μίας που έδειχνε ζευγάρι να κοιτάει χάρτη, πρόσωπα εκτώς πλάνου).</p>

<p>Καμία φόρμα επικοινωνίας. Αντί αυτού, γραπτές οδηγίες για το τι να γράψεις στο email που θα στείλεις για να ζητήσεις μεταφορά.</p>

<p>Χωρίς analytics, χωρίς φόρμα, χωρίς τρόπο να μετρήσει αν το site δουλεύει.</p>

<h2 id="η-διαφορά">Η διαφορά</h2>

<h3 id="τυπικό-template-site-300500-">Τυπικό template site (300–500 €)</h3>
<ul>
  <li>Γενικό template με αλλαγμένα χρώματα</li>
  <li>Stock εικόνες που δεν σχετίζονται με την επιχείρηση</li>
  <li>“Εντυπωσιακά” εφέ που κάνουν το site αργό και δύσχρηστο</li>
</ul>

<p><a href="https://www.google.com/search?q=free+bootstrap+marketing+templates&amp;udm=2&amp;sclient=img">Κάνε μια αναζήτηση για “free bootstrap marketing templates”</a> και θα σου φανούν πολύ γνώριμα.</p>

<h3 id="στρατηγικό-site-1000--">Στρατηγικό site (1000 € +)</h3>
<ul>
  <li>Κάθε στοιχείο υπάρχει για συγκεκριμένο λόγο</li>
  <li>Εξυπηρετεί τους επισκέπτες ώστε να γίνουν πελάτες</li>
  <li>Ευανάγνωστο, λειτουργικό, και χτισμένο για τις δικές σου ανάγκες</li>
</ul>

<p><a href="/en/snippets">Δες παραδείγματα από το portfolio μου</a></p>

<h2 id="τι-σημαίνει-custom-design">Τι σημαίνει custom design</h2>
<ul>
  <li><strong>Λογική Δομή</strong>: Αν είσαι εστιατόριο, το μενού είναι άμεσα προσβάσιμο (και όχι PDF). Αν είσαι δικηγόρος, οι υπηρεσίες σου να είναι κατανοητές.</li>
  <li><strong>Λύσεις που δουλεύουν</strong>: Οι επισκέπτες βρίσκουν αυτό που ψάχνουν γρήγορα, παίρνουν δράση, και γίνονται πελάτες.</li>
  <li><strong>Χωρίς περιττά στοιχεία</strong>: Κάθε σελίδα και πληροφορία εξυπηρετεί έναν σκοπό. Αυτό δεν σημαίνει ότι το site είναι απαραίτητα απλό ή minimal—τα ωραία γραφικά εξυπηρετούν και αυτά σκοπό.</li>
</ul>

<h2 id="δεν-ξεκίνησα-ως-designer">Δεν ξεκίνησα ως designer</h2>

<p>Πριν το web development δούλευα σε supply chain και operations. Έχω δει πρώτο χέρι πώς κακοσχεδιασμένα συστήματα σπαταλούν χρόνο και χρήμα. Το ίδιο ισχύει και για website — αν δεν δουλεύει, κοστίζει.</p>

<p>Δεν έχω στόχο να φτιάχνω «ωραία» site που δεν κάνουν τίποτα, αλλά όμορφα και χρηστικά εργαλεία που δουλεύουν.</p>

<h2 id="site-που-φέρνει-πελάτες">Site που φέρνει πελάτες</h2>

<ul>
  <li>Τιμή για site 1-3 σελίδων: <strong>1.000 €</strong></li>
  <li>Για πολυσέλιδα projects: <strong>2.000-3.000 €</strong> ή παραπάνω ανάλογα την πολυπλοκότητα</li>
</ul>

<p>Αυτό περιλαμβάνει:</p>

<ul>
  <li>Ανάλυση του χώρου σου και του ανταγωνισμού</li>
  <li>Στρατηγικό σχεδιασμό και προγραμματισμό από το μηδέν</li>
  <li>Βελτιστοποίηση για όλες τις συσκευές</li>
  <li>Συγγραφή ή βελτίωση κειμένων, δημιουργία εικόνων κατάλληλων για web</li>
  <li>Μπόλικο testing για να μην υπάρχουν εκπλήξεις</li>
</ul>

<p>Θα μπορούσα να φτιάχνω copy-paste sites με template, να τελειώνω γρήγορα και να πληρώνομαι το ίδιο. Αλλά δεν μπορώ να το κάνω με ήσυχη συνείδηση.</p>

<h2 id="εν-κατακλείδι">Εν κατακλείδι</h2>

<p>Αν θες ένα site απλά για να υπάρχει, δεν είμαι η σωστή επιλογή. Αν θες ένα site που να δουλεύει για σένα, θα χαρώ να μιλήσουμε.</p>

<hr />

<p><a href="/en/contact/">Στοιχεία επικοινωνίας →</a></p>

<p><a href="/en">Αρχική σελίδα (Αγγλικά) →</a></p>

<!-- [English version →](/en/mle) -->]]></content><author><name></name></author><category term="el" /><category term="business" /><summary type="html"><![CDATA[Πελάτης μου στον χώρο μεταφορών είχε ήδη site. Αρχική σελίδα με καρουζέλ γεμάτο stock φωτογραφίες (συμπεριλαμβανομένης μίας που έδειχνε ζευγάρι να κοιτάει χάρτη, πρόσωπα εκτώς πλάνου).]]></summary></entry><entry xml:lang="en"><title type="html">Solo subscription agencies</title><link href="https://annafilou.com/en/solo-agencies" rel="alternate" type="text/html" title="Solo subscription agencies" /><published>2025-05-12T00:00:00+00:00</published><updated>2025-05-12T00:00:00+00:00</updated><id>https://annafilou.com/en/solo-agencies</id><content type="html" xml:base="https://annafilou.com/en/solo-agencies"><![CDATA[<p>Lately I’ve been seeing more and more solo designers offering design “subscriptions”: fixed monthly fee, one request at a time, cancel anytime. It’s basically a retainer plan, rebranded.</p>

<p>And the model makes sense! Especially for early-stage startups who need ongoing design support but might not want to hire someone full-time for a variety of reasons—including not knowing whether the business will still be around five months down the line. (Which probably explains why this setup is more common in the US, from my limited perspective, where startups come and go by the minute.)</p>

<h2 id="the-marketing-rubs-me-the-wrong-way">The marketing rubs me the wrong way</h2>
<p>A lot of these are solo designers calling themselves an “agency.” Now, sometimes a single freelancer has a network of collaborators they bring in as needed. Or they’re just starting out and looking to grow soon. That’s a bit of a gray area, and in that case “studio” or “agency” might make sense.</p>

<p>But that’s not what I’m referring to here. I’m talking about people who proudly say, “When you work with Studio XYZ, you work only with me. No other collaborators.” So far, still fine!</p>

<p>What’s <strong>not</strong> fine, in my opinion, is writing—on the same landing page—something like:</p>
<blockquote>
  <p>“Ditch unreliable freelancers today and work with Studio XYZ instead!”</p>
</blockquote>

<p>But… <strong>you</strong> are freelancer too! 😅 You’re just hoping prospective clients won’t notice. Sure, it’s technically stated that they’ll be working with you alone, but then why present yourself as an agency in the first place?</p>

<p>That’s what bugs me. Not the model itself, but the almost dishonest positioning.</p>

<h2 id="design--task-delivery">Design = task delivery?</h2>

<p>Then there’s the way the service is often structured: You pay → You get a Trello board → You add cards → You get “deliverables”</p>

<p>Simple. Transactional. Trello becomes a prompting box and the designer becomes ChatGPT.</p>

<p>Maybe there’s more going on behind the scenes though. Maybe the designers hop on calls, ask thoughtful questions, and push back when needed, and Trello is simply their project management tool of choice! But that’s not the idea you get from the landing pages. They pitch the service as a UI vending machine, so I’m inclined to believe that’s what it is.</p>

<p>That messaging shapes expectations; not just those of the given clients, but of the entire industry. It reinforces the idea that the value of design lies in how many mockups you can produce, and how quickly.</p>

<h2 id="full-timer-vs-me">Full-timer vs “me”</h2>

<p>Another pattern I’ve noticed: almost every one of these sites has an FAQ section that starts with:
<strong>“Why not hire a full-time designer instead?”</strong> And the answer is always some variation of:
<strong>“Because a full-time hire costs $100K+/year, and I only cost $5K/month.”</strong></p>

<p>Which might sound like a deal for a sec; until you remember that $5K month is $60K/year—for part-time availability. Not such a dramatic difference.</p>

<p>Of course, not everyone needs a full-time designer! In many cases, working with a freelancer or a small studio makes more sense. But if your selling point is “I’m the same but cheaper” (and that isn’t even entirely accurate) I don’t think you’re doing anyone a service.</p>

<h2 id="clarifications">Clarifications</h2>

<p>To be clear: I <strong>love</strong> the idea of making good design more accessible, and retainer models are great. Also, sometimes a client simply needs some mockups, shipped fast. Maybe to show them around and validate an early idea; perhaps to put a fresh coat of paint onto a legacy app that they don’t have the resources to fully remake just yet. And that’s totally fine! But these subscription services are often presented as the <strong>ideal</strong> setup for every company, and that’s just misleading.</p>

<p>Basically: you wanna sell design subscriptions? Totally fair and welcome! Just don’t be sleazy about it and be mindful of what expectations you’re creating.</p>

<hr />

<p><strong>Curious to hear from you:</strong></p>

<ul>
  <li>Have you worked with a one-person design subscription “agency”?</li>
  <li>Do you offer this kind of service yourself? Am I getting it wrong?</li>
  <li>What’s your take—good, harmful, or something in between?</li>
</ul>

<p>You know where to find my email ^-^</p>]]></content><author><name></name></author><category term="en" /><category term="business" /><summary type="html"><![CDATA[Lately I’ve been seeing more and more solo designers offering design “subscriptions”: fixed monthly fee, one request at a time, cancel anytime. It’s basically a retainer plan, rebranded.]]></summary></entry><entry xml:lang="en"><title type="html">Tailwind isn’t a Design System (and that’s okay)</title><link href="https://annafilou.com/en/tailwind" rel="alternate" type="text/html" title="Tailwind isn’t a Design System (and that’s okay)" /><published>2025-05-05T00:00:00+00:00</published><updated>2025-05-05T00:00:00+00:00</updated><id>https://annafilou.com/en/tailwind</id><content type="html" xml:base="https://annafilou.com/en/tailwind"><![CDATA[<p>I’m a big fan of utility class CSS. I use Tachyons—a lightweight alternative to Tailwind—on my personal site. It’s dead simple and saves me time and effort. But while utility classes are great for solo projects, things get more complicated when you try to scale them across a large team.</p>

<p>This isn’t a critique of Tailwind or utility classes as a concept—they’re great, especially for solo developers or very small teams. But in larger organizations, like when you have a frontend team of 10+ developers, Tailwind can cause just as many problems as it solves.</p>

<p>Let me explain.</p>

<hr />

<h2 id="whats-the-allure-of-tailwind">What’s the allure of Tailwind?</h2>

<p>Speed and perceived simplicity.</p>

<p>Tailwind’s big selling point is that developers don’t have to write custom CSS. You see a design in Figma, and you translate it directly: <code class="language-plaintext highlighter-rouge">p-4</code> for 16px padding, <code class="language-plaintext highlighter-rouge">bg-white</code> for a white background, and so on. This can help avoid situations where different developers name similar classes differently—like one using <code class="language-plaintext highlighter-rouge">.card-padding</code> and another using <code class="language-plaintext highlighter-rouge">.tile-padding</code> for the exact same spacing.</p>

<p>So far, so good.</p>

<p>But what happens when a design calls for 15px padding? One dev might round up to <code class="language-plaintext highlighter-rouge">p-4</code> (16px), another might round down to <code class="language-plaintext highlighter-rouge">p-3</code> (12px), and yet another might use <code class="language-plaintext highlighter-rouge">p-[15px]</code>, effectively creating a new class.</p>

<p>Now, you might say, “If the team agreed to use Tailwind, then designs in Figma shouldn’t use 15px padding in the first place.” Fair—but it’s not that simple. (See <a href="#code-enforces-consistency">“Consistency through code”</a> below.)</p>

<p>Say you want to change all card backgrounds from white to grey. You search for <code class="language-plaintext highlighter-rouge">bg-white</code>, but now you risk changing buttons or other sections too—because utility classes don’t tell you <em>where</em> they’re used. Tailwind doesn’t distinguish between a card, a section, or a button. That knowledge lives in the developer’s head (or in documentation, which starts to defeat the purpose of using Tailwind. More on that below.)</p>

<hr />

<h2 id="utility-classes-arent-a-substitute-for-a-design-system">Utility classes aren’t a substitute for a design system</h2>

<p>Many teams adopt Tailwind because it’s “flexible.” You can build “anything” with it. But that’s exactly the problem. You’re not building <em>anything</em>—you’re building <strong>a very specific thing</strong>. So you don’t need flexibility—you need <strong>consistency</strong>.</p>

<p>Tailwind’s verbose syntax becomes more unwieldy when you factor in dark mode and responsive design.</p>

<p>Want a background that’s light red in light mode and dark red in dark mode?</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bg-red-200 dark:bg-red-800
</code></pre></div></div>

<p>Want card padding to adjust between mobile and desktop?</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sm:p-2 lg:p-4
</code></pre></div></div>

<p>That’s fast to write—you don’t need to create custom classes or media queries.</p>

<blockquote>
  <p>But… where does the mapping between <code class="language-plaintext highlighter-rouge">red-200</code> and <code class="language-plaintext highlighter-rouge">red-800</code> live? Or between <code class="language-plaintext highlighter-rouge">p-2</code> and <code class="language-plaintext highlighter-rouge">p-4</code>?</p>
</blockquote>

<p>In the developer’s or designer’s head!</p>

<p>Unless you document it. But once you’re writing docs to enforce consistent use of utility classes, are you really saving time? You’re actually building your own design system <strong>on top of</strong> Tailwind.</p>

<p>Compare that with using a semantic class like <code class="language-plaintext highlighter-rouge">product-card</code>, which already handles different themes and viewports behind the scenes.</p>

<hr />

<h2 id="code-enforces-consistency">Code enforces consistency</h2>

<p>Figma designs <strong>should</strong> aim for consistency, but ultimately, code is the final product. A perfectly consistent Figma file is irrelevant if the implementation isn’t. Likewise, inconsistencies in Figma don’t matter <strong>as long as</strong> they don’t carry through to production code.</p>

<p>The idea that Figma should be the single source of truth for consistency falls short. Figma has helpful tools like components, styles, and now variables—but it’s still easy to accidentally “detach” things, especially during experimentation. Code, on the other hand, requires deliberate effort to introduce inconsistencies. Accidentally typing <code class="language-plaintext highlighter-rouge">p-13</code> is far less likely than detaching a style variable in Figma.</p>

<p><strong>Often, the push to create a super-flexible CSS system stems from a lack of collaboration between design and dev teams.</strong> A dev might think, “Who knows what the designers will change next? We need something that can handle anything.” But imagine if both teams worked together to define naming conventions and constraints from the start!</p>

<p>A well-defined system can live in both CSS <strong>and</strong> Figma. It helps both sides stay aligned. Naming variables semantically—like <code class="language-plaintext highlighter-rouge">padding-card</code> or <code class="language-plaintext highlighter-rouge">padding-section</code>—provides a built-in check. If someone tries to use <code class="language-plaintext highlighter-rouge">padding-section</code> on a product card, it raises red flags.</p>

<hr />

<h2 id="what-utility-classes-are-great-for">What utility classes are GREAT for</h2>

<ul>
  <li><strong>CSS Grid</strong>. I’ll take <code class="language-plaintext highlighter-rouge">grid grid-cols-2 sm:grid-cols-3</code> over one-off CSS classes any day.</li>
  <li><strong>Solo projects</strong>. On my personal site, utility classes are perfect. I know the patterns, and if I use 12px padding on one page and 16px on another, that’s fine. It’s a small project.</li>
  <li><strong>Spacing</strong>, with constraints. Once you define custom utility classes or CSS variables for spacing—like <code class="language-plaintext highlighter-rouge">padding-card</code>, <code class="language-plaintext highlighter-rouge">padding-section</code>—you can apply them consistently across components and breakpoints.</li>
</ul>

<p>The issue isn’t utility classes themselves. It’s relying on an enormous set of them without any guidelines.</p>

<hr />

<h2 id="tldr">TL;DR.</h2>

<p>You pay for shortcuts down the road. Tailwind’s flexibility becomes a liability when you’re trying to build something consistent with a team.</p>

<p>The ability to build “anything” is less valuable than the ability to build <strong>one thing well</strong>. And doing that requires adding constraints—not avoiding them.</p>]]></content><author><name></name></author><category term="en" /><category term="code" /><summary type="html"><![CDATA[I’m a big fan of utility class CSS. I use Tachyons—a lightweight alternative to Tailwind—on my personal site. It’s dead simple and saves me time and effort. But while utility classes are great for solo projects, things get more complicated when you try to scale them across a large team.]]></summary></entry></feed>