Hire a web Developer and Designer to upgrade and boost your online presence with cutting edge Technologies

Thursday, November 7, 2024

Email Services justification

 

When comparing Mailchimp and GoDaddy Email Services, it's important to focus on the specific needs and goals of your client, as both platforms offer distinct features that can be beneficial depending on the use case. Here’s a breakdown to help you convince a client to use Mailchimp over GoDaddy Email for their website, as well as provide alternatives that could be beneficial in certain cases.

1. Key Differences Between Mailchimp and GoDaddy Email Services

Mailchimp:

  • Specialized for Email Marketing: Mailchimp is primarily known for its email marketing features, offering tools to design campaigns, automate emails, segment audiences, track results, and more. While Mailchimp started as an email service provider (ESP), it has grown to include more tools for marketing automation and customer relationship management (CRM).

  • Advanced Analytics: Mailchimp offers advanced reporting and analytics, which allows you to track email performance, audience engagement, conversion rates, and more. This is valuable for clients who need insights into their campaigns to improve their marketing strategies.

  • Ease of Use: Mailchimp’s drag-and-drop editor for creating newsletters and marketing emails is user-friendly, even for non-technical users. It’s a great option for small businesses looking to run campaigns with little technical knowledge.

  • Automation: Mailchimp’s marketing automation features (e.g., triggered emails, welcome series, abandoned cart reminders) allow businesses to set up automated email workflows, which helps engage customers without manual intervention.

  • Free Plan Available: Mailchimp’s free plan includes 500 subscribers and 3,500 emails per month, with access to basic email marketing features. This is an excellent starting point for small businesses or startups.

GoDaddy Email Services:

  • Primarily for Hosting and Basic Email: GoDaddy’s email offerings (e.g., Office 365, Professional Email) are typically email hosting services bundled with website hosting or domain registration. They are useful for clients who want a business email like name@domain.com, but lacking advanced marketing features.

  • Basic Features: GoDaddy's email services are mostly focused on providing reliable email hosting, calendar integration, address book management, and basic spam filtering. It doesn’t provide the rich features that Mailchimp does for marketing automation or advanced email tracking.

  • Price: GoDaddy’s email service is usually bundled with hosting plans or can be purchased as a standalone service, often at a higher price compared to similar offerings. For clients only looking for a basic email service without marketing capabilities, GoDaddy might be an option, but they lack some of the added value Mailchimp brings to email marketing.

2. Convincing the Client to Use Mailchimp Over GoDaddy Email Service

Here’s how you might position Mailchimp as a better choice over GoDaddy Email for your client:

a) If the Client’s Focus is Email Marketing or Campaigns:

  • Rich Features for Email Marketing: If your client is looking for a way to not just send emails but also grow their business through marketing campaigns, Mailchimp’s advanced features (e.g., segmentation, automation, landing pages, A/B testing) will be far more useful than GoDaddy’s basic email offering.

  • Analytics & Reporting: Show your client how Mailchimp’s analytics can provide valuable insights, helping them track their email campaign performance, optimize their strategy, and improve engagement rates. GoDaddy doesn’t offer robust email campaign analytics.

  • Email Automation: For a client who wants to automate emails based on specific triggers (e.g., welcome email, abandoned cart email, etc.), Mailchimp’s automation features are highly valuable. GoDaddy does not provide this kind of marketing-focused automation.

  • User-Friendly Design: If the client doesn’t have technical expertise but needs to send attractive emails or newsletters, Mailchimp’s drag-and-drop email builder and pre-built templates are perfect for them. GoDaddy’s email platform lacks these types of marketing-centric design features.

b) If the Client Needs Business-Grade Email Hosting:

  • Separate Needs for Business Emails: If the client’s primary need is not marketing but just business communication (e.g., a professional email with their domain), GoDaddy’s Professional Email or Office 365 might still be a good option. You could suggest this service if their focus is simply email hosting with domain support (e.g., contact@company.com).

However, you could recommend Mailchimp for the client’s marketing needs and use GoDaddy (or another provider) for their basic email.

c) Cost Considerations:

  • Free Plan & Scalable Options: For businesses starting small or with a low budget, Mailchimp's free plan offers 500 subscribers and 3,500 emails/month, while GoDaddy charges for email hosting, which may not justify the cost if the client is only sending marketing emails.

  • Pricing Transparency: Mailchimp provides clear pricing tiers based on subscribers and emails sent, allowing clients to scale as their business grows. GoDaddy may be more opaque with pricing structures, depending on which plan your client chooses.

3. Alternative Free Email Services to Suggest for Specific Needs

If Mailchimp isn’t the right fit for the client or if they are looking for other free email services (for general communication), here are a few alternatives to consider:

a) Gmail (Google Workspace)

  • Free & Paid Options: Gmail offers personal and business email services. The free Gmail service is widely popular, and the business version (Google Workspace) provides more features like a custom domain, calendar, cloud storage, etc.
  • User-Friendly: Gmail is known for its simplicity, reliability, and integration with other Google services, which may be helpful for your client if they use Google tools like Docs, Sheets, or Drive.

b) Zoho Mail

  • Free Plan: Zoho offers a free email hosting plan for up to 5 users with 5GB of storage per user, a great option for small businesses.
  • Advanced Features: Zoho also offers advanced features such as email routing, filters, and calendar integrations with additional options in their paid plans.

c) ProtonMail

  • Secure Email: ProtonMail is free for personal use, with additional privacy features like end-to-end encryption.
  • Business Option: ProtonMail also offers paid plans for businesses that prioritize secure and private email communication.

d) Outlook (Microsoft 365)

  • Microsoft Email Hosting: If your client is already using Microsoft products, Outlook is a reliable option. They provide free Outlook.com accounts and paid Microsoft 365 Business plans for more features like custom domains, cloud storage, etc.

e) Sendinblue

  • Email Marketing Free Plan: Sendinblue offers email marketing with a free plan that allows up to 300 emails/day to unlimited contacts, which is ideal for businesses that want to send newsletters or marketing campaigns on a smaller scale.

4. AI Techniques to Optimize Email Marketing Automation in Real-Time

To further optimize and modernize email marketing for your clients using AI and automation, here are some AI-powered techniques you could leverage:

  1. Personalized Content and Segmentation:

    • Use AI to analyze customer behavior, segment audiences into specific categories (e.g., demographics, interests, purchase history), and send personalized email content based on this data.
    • Predictive Analytics: AI can predict the best time to send emails or which products or content to recommend based on past user behavior.
  2. A/B Testing Automation:

    • AI can automatically conduct A/B tests on subject lines, content, and send times to optimize future email campaigns for higher engagement rates.
  3. Dynamic Email Content:

    • AI can help generate dynamic email content that changes based on the recipient's preferences, location, or behavior. For example, showing personalized discounts or recommended products to users based on their past interactions with the brand.
  4. Sentiment Analysis:

    • Implement sentiment analysis using AI to understand how users feel about certain email campaigns and adjust strategies accordingly. This can improve engagement and customer loyalty over time.
  5. Automation of Email Workflows:

    • Automate email workflows (e.g., welcome series, abandoned cart emails, post-purchase follow-ups) to improve conversion rates and customer retention using AI-based tools.

Conclusion:

  • If your client needs advanced email marketing features such as automation, segmentation, and analytics, Mailchimp is the better choice over GoDaddy Email Services. It’s especially great for businesses looking to grow through email marketing rather than just basic email hosting.

  • For those focused on simple business emails without a heavy marketing focus, services like Gmail, Zoho Mail, or ProtonMail might be more appropriate.

  • AI techniques can be applied to optimize email marketing automation, improve personalization, and maximize engagement with the audience, regardless of the platform used.

By understanding your client's needs and explaining the value proposition of each service, you can make an informed recommendation that aligns with their goals.

Wednesday, October 23, 2024

CSS min() All The Things

 experiments with the CSS min() function, exploring its flexibility with different units to determine if it is the be-all, end-all for responsiveness. Discover the cautions he highlights against dogmatic approaches to web design based on his findings.

Did you see this post that Chris Coyier published back in August? He experimented with CSS container query units, going all in and using them for every single numeric value in a demo he put together. And the result was… not too bad, actually.

See the Pen Container Units for All Units [forked] by Chris Coyier.

What I found interesting about this is how it demonstrates the complexity of sizing things. We’re constrained to absolute and relative units in CSS, so we’re either stuck at a specific size (e.g., px) or computing the size based on sizing declared on another element (e.g., %, em, rem, vw, vh, and so on). Both come with compromises, so it’s not like there is a “correct” way to go about things — it’s about the element’s context — and leaning heavily in any one direction doesn’t remedy that.

I thought I’d try my own experiment but with the CSS min() function instead of container query units. Why? Well, first off, we can supply the function with any type of length unit we want, which makes the approach a little more flexible than working with one type of unit. But the real reason I wanted to do this is personal interest more than anything else.

The Demo

I won’t make you wait for the end to see how my min() experiment went:


We’ll talk about that more after we walk through the details.

A Little About min()

The min() function takes two values and applies the smallest one, whichever one happens to be in the element’s context. For example, we can say we want an element to be as wide as 50% of whatever container it is in. And if 50% is greater than, say 200px, cap the width there instead.

See the Pen [forked] by Geoff Graham.

So, min() is sort of like container query units in the sense that it is aware of how much available space it has in its container. But it’s different in that min() isn’t querying its container dimensions to compute the final value. We supply it with two acceptable lengths, and it determines which is best given the context. That makes min() (and max() for that matter) a useful tool for responsive layouts that adapt to the viewport’s size. It uses conditional logic to determine the “best” match, which means it can help adapt layouts without needing to reach for CSS media queries.

.element {
  width: min(200px, 50%);
}

/* Close to this: */
.element {
  width: 200px;

  @media (min-width: 600px) {
    width: 50%;
  }
}

The difference between min() and @media in that example is that we’re telling the browser to set the element’s width to 50% at a specific breakpoint of 600px. With min(), it switches things up automatically as the amount of available space changes, whatever viewport size that happens to be.

When I use the min(), I think of it as having the ability to make smart decisions based on context. We don’t have to do the thinking or calculations to determine which value is used. However, using min() coupled with just any CSS unit isn’t enough. For instance, relative units work better for responsiveness than absolute units. You might even think of min() as setting a maximum value in that it never goes below the first value but also caps itself at the second value.

I mentioned earlier that we could use any type of unit in min(). Let’s take the same approach that Chris did and lean heavily into a type of unit to see how min() behaves when it is used exclusively for a responsive layout. Specifically, we’ll use viewport units as they are directly relative to the size of the viewport.

Now, there are different flavors of viewport units. We can use the viewport’s width (vw) and height (vh). We also have the vmin and vmax units that are slightly more intelligent in that they evaluate an element’s width and height and apply either the smaller (vmin) or larger (vmax) of the two. So, if we declare 100vmax on an element, and that element is 500px wide by 250px tall, the unit computes to 500px.

That is how I am approaching this experiment. What happens if we eschew media queries in favor of only using min() to establish a responsive layout and lean into viewport units to make it happen? We’ll take it one piece at a time.

Font Sizing

There are various approaches for responsive type. Media queries are quickly becoming the “old school” way of doing it:

p { font-size: 1.1rem; }

@media (min-width: 1200px) {
  p { font-size: 1.2rem; }
}

@media (max-width: 350px) {
  p { font-size: 0.9rem; }
}

Sure, this works, but what happens when the user uses a 4K monitor? Or a foldable phone? There are other tried and true approaches; in fact, clamp() is the prevailing go-to. But we’re leaning all-in on min(). As it happens, just one line of code is all we need to wipe out all of those media queries, substantially reducing our code:

p { font-size: min(6vmin, calc(1rem + 0.23vmax)); }

I’ll walk you through those values…

  1. 6vmin is essentially 6% of the browser’s width or height, whichever is smallest. This allows the font size to shrink as much as needed for smaller contexts.
  2. For calc(1rem + 0.23vmax), 1rem is the base font size, and 0.23vmax is a tiny fraction of the viewport‘s width or height, whichever happens to be the largest.
  3. The calc() function adds those two values together. Since 0.23vmax is evaluated differently depending on which viewport edge is the largest, it’s crucial when it comes to scaling the font size between the two arguments. I’ve tweaked it into something that scales gradually one way or the other rather than blowing things up as the viewport size increases.
  4. Finally, the min() returns the smallest value suitable for the font size of the current screen size.

And speaking of how flexible the min() approach is, it can restrict how far the text grows. For example, we can cap this at a maximum font-size equal to 2rem as a third function parameter:

p { font-size: min(6vmin, calc(1rem + 0.23vmax), 2rem); }

This isn’t a silver bullet tactic. I’d say it’s probably best used for body text, like paragraphs. We might want to adjust things a smidge for headings, e.g., <h1>:

h1 { font-size: min(7.5vmin, calc(2rem + 1.2vmax)); }

We’ve bumped up the minimum size from 6vmin to 7.5vmin so that it stays larger than the body text at any viewport size. Also, in the calc(), the base size is now 2rem, which is smaller than the default UA styles for <h1>. We’re using 1.2vmax as the multiplier this time, meaning it grows more than the body text, which is multiplied by a smaller value, .023vmax.

This works for me. You can always tweak these values and see which works best for your use. Whatever the case, the font-size for this experiment is completely fluid and completely based on the min() function, adhering to my self-imposed constraint.

Margin And Padding 

Spacing is a big part of layout, responsive or not. We need margin and padding to properly situate elements alongside other elements and give them breathing room, both inside and outside their box.

We’re going all-in with min() for this, too. We could use absolute units, like pixels, but those aren’t exactly responsive.

min() can combine relative and absolute units so they are more effective. Let’s pair vmin with px this time:

div { margin: min(10vmin, 30px); }

10vmin is likely to be smaller than 30px when viewed on a small viewport. That’s why I’m allowing the margin to shrink dynamically this time around. As the viewport size increases, whereby 10vmin exceeds 30px, min() caps the value at 30px, going no higher than that.

Notice, too, that I didn’t reach for calc() this time. Margins don’t really need to grow indefinitely with screen size, as too much spacing between containers or elements generally looks awkward on larger screens. This concept also works extremely well for padding, but we don’t have to go there. Instead, it might be better to stick with a single unit, preferably em, since it is relative to the element’s font-size. We can essentially “pass” the work that min() is doing on the font-size to the margin and padding properties because of that.

.card-info {
  font-size: min(6vmin, calc(1rem + 0.12vmax));
  padding: 1.2em;
}

Now, padding scales with the font-size, which is powered by min().

Widths

Setting width for a responsive design doesn’t have to be complicated, right? We could simply use a single percentage or viewport unit value to specify how much available horizontal space we want to take up, and the element will adjust accordingly. Though, container query units could be a happy path outside of this experiment.

But we’re min() all the way!

min() comes in handy when setting constraints on how much an element responds to changes. We can set an upper limit of 650px and, if the computed width tries to go larger, have the element settle at a full width of 100%:

.container { width: min(100%, 650px); }

Things get interesting with text width. When the width of a text box is too long, it becomes uncomfortable to read through the texts. There are competing theories about how many characters per line of text is best for an optimal reading experience. For the sake of argument, let’s say that number should be between 50-75 characters. In other words, we ought to pack no more than 75 characters on a line, and we can do that with the ch unit, which is based on the 0 character’s size for whatever font is in use.

p {
  width: min(100%, 75ch);
}

This code basically says: get as wide as needed but never wider than 75 characters.

Sizing Recipes Based On min()

Over time, with a lot of tweaking and modifying of values, I have drafted a list of pre-defined values that I find work well for responsively styling different properties:

:root {
  --font-size-6x: min(7.5vmin, calc(2rem + 1.2vmax));
  --font-size-5x: min(6.5vmin, calc(1.1rem + 1.2vmax));
  --font-size-4x: min(4vmin, calc(0.8rem + 1.2vmax));
  --font-size-3x: min(6vmin, calc(1rem + 0.12vmax));
  --font-size-2x: min(4vmin, calc(0.85rem + 0.12vmax));
  --font-size-1x: min(2vmin, calc(0.65rem + 0.12vmax));
  --width-2x: min(100vw, 1300px);
  --width-1x: min(100%, 1200px);
  --gap-3x: min(5vmin, 1.5rem);
  --gap-2x: min(4.5vmin, 1rem);
  --size-10x: min(15vmin, 5.5rem);
  --size-9x: min(10vmin, 5rem);
  --size-8x: min(10vmin, 4rem);
  --size-7x: min(10vmin, 3rem);
  --size-6x: min(8.5vmin, 2.5rem);
  --size-5x: min(8vmin, 2rem);
  --size-4x: min(8vmin, 1.5rem);
  --size-3x: min(7vmin, 1rem);
  --size-2x: min(5vmin, 1rem);
  --size-1x: min(2.5vmin, 0.5rem);
}

This is how I approached my experiment because it helps me know what to reach for in a given situation:

h1 { font-size: var(--font-size-6x); }

.container {
  width: var(--width-2x);
  margin: var(--size-2x);
}

.card-grid { gap: var(--gap-3x); }

There we go! We have a heading that scales flawlessly, a container that’s responsive and never too wide, and a grid with dynamic spacing — all without a single media query. The --size- properties declared in the variable list are the most versatile, as they can be used for properties that require scaling, e.g., margins, paddings, and so on.

The Final Result, Again

I shared a video of the result, but here’s a link to the demo.

See the Pen min() website [forked] by Vayo.

So, is min() the be-all, end-all for responsiveness? Absolutely not. Neither is a diet consisting entirely of container query units. I mean, it’s cool that we can scale an entire webpage like this, but the web is never a one-size-fits-all beanie.

If anything, I think this and what Chris demoed are warnings against dogmatic approaches to web design as a whole, not solely unique to responsive design. CSS features, including length units and functions, are tools in a larger virtual toolshed. Rather than getting too cozy with one feature or technique, explore the shed because you might find a better tool for the job.

It’s Here! How To Measure UX & Design Impact

Design decisions shouldn’t be a matter of personal preference. We can use reliable design KPIs and UX metrics to guide and shape our design work and measure its impact on business. Meet How To Measure UX and Design Impact, our new video course that helps with just that.

Finally! After so many years, we’re very happy to launch “How To Measure UX and Design Impact”, our new practical guide for designers and managers on how to set up and track design success in your company — with UX scorecards, UX metrics, the entire workflow and Design KPI trees. Neatly put together by yours truly, Vitaly Friedman. Jump to details.

How to Measure UX and Design Impact, with Vitaly Friedman.

Video + UX Training

$ 495.00 $ 799.00 Get Video + UX Training

25 video lessons (8h) + Live UX Training.
100 days money-back-guarantee.

Video only

$ 250.00$ 395.00
Get the video course

25 video lessons (8h). Updated yearly.
Also available as a UX Bundle with 2 video courses.

The Backstory #

In many companies, designers are perceived as disruptors, rather than enablers. Designers challenge established ways of working. They ask a lot of questions — much needed ones but also uncomfortable ones. They focus “too much” on user needs, pushing revenue projections back, often with long-winded commitment to testing and research and planning and scoping.

Almost every department in almost every company has their own clearly defined objectives, metrics and KPIs. In fact, most departments — from finance to marketing to HR to sales — are remarkably good at visualizing their impact and making it visible throughout the entire organization.

“How To Measure UX and Design Impact” with Vitaly Friedman
Designing a KPI tree, an example of how to connect business objectives with design initiatives through the lens of design KPIs. (Large preview)

But as designers, we rarely have a set of established Design KPIs that we regularly report to senior management. We don’t have a clear definition of design success. And we rarely measure the impact of our work once it’s launched. So it’s not surprising that moste parts of the business barely know what we actually do all day long.

Business wants results. It also wants to do more of what has worked in the past. But it doesn’t want to be disrupted — it wants to disrupt. It wants to reduce time to market and minimize expenses; increase revenue and existing business, find new markets. This requires fast delivery and good execution.

And that’s what we are often supposed to be — good “executors”. Or to put differently, “pixel pushers”.

“How To Measure UX and Design Impact” with Vitaly Friedman
UX scorecards for critical tasks, along with a few UX metrics in a simple spreadsheet. From Matthew Garvin’s work on UX scorecards.

Over years, I’ve been searching for a way to change that. This brought me to Design KPIs and UX scorecards, and a workflow to translate business goals into actionable and measurable design initiatives. I had to find a way to explain, visualize and track that incredible impact that designers have on all parts of business — from revenue to loyalty to support to delivery.

The results of that journey are now public in our new video course: “How To Measure UX and Design Impact” — a practical guide for designers, researchers and UX leads to measure and visualize UX impact on business.

About The Course #

The course dives deep into establishing team-specific design KPIs, how to track them effectively, how to set up ownership and integrate metrics in design process. You’ll discover how to translate ambiguous objectives into practical design goals, and how to measure design systems and UX research.

Also, we’ll make sense of OKRs, Top Task Analysis, SUS, UMUX-Lite, UEQ, TPI, KPI trees, feedback scoring, gap analysis, and Kano model — and what UX research methods to choose to get better results. Jump to the table of contents or get your early-bird.

“How To Measure UX and Design Impact” with Vitaly Friedman
The setup for the video recordings. Once all content is in place, it’s about time to set up the recording.

A practical guide to UX metrics and Design KPIs
8h-video course + live UX training. Free preview.

  • 25 chapters (8h), with videos added/updated yearly
  • Free preview, examples, templates, workflows
  • No subscription: get once, access forever
  • Life-time access to all videos, slides, checklists.
  • Add-on: live UX training, running 2× a year
  • Use the code SMASHING to get 20% off today
  • Jump to the details →

Table of Contents #

25 chapters, 8 hours, with practical examples, exercises, and everything you need to master the art of measuring UX and design impact. Don’t worry, even if it might seem overwhelming at first, we’ll explore things slowly and thoroughly. Taking 1–2 sessions per week is a perfectly good goal to aim for.

A sneak-peek inside the video course.
We can’t improve without measuring. That’s why our new video course gives you the tools you need to make sense of it all: user needs, just like business needs. (View large version)

Who Is The Course For? #

This course is tailored for advanced UX practitioners, design leaders, product managers, and UX researchers who are looking for a practical guide to define, establish and track design KPIs, translate business goals into actionable design tasks, and connect business needs with user needs.

What You’ll Learn #

By the end of the video course, you’ll have a packed toolbox of practical techniques and strategies on how to define, establish, sell, and measure design KPIs from start to finish — and how to make sure that your design work is always on the right trajectory. You’ll learn:

  • How to translate business goals to UX initiatives,
  • The difference between OKRs, KPIs, and metrics,
  • How to define design success for your company,
  • Metrics and KPIs that businesses typically measure,
  • How to choose the right set of metrics and KPIs,
  • How to establish design KPIs focused on user needs,
  • How to build a comprehensive design KPI tree,
  • How to combine qualitative and quantitative insights,
  • How to choose and prioritize design work,
  • How to track the impact of design work on business goals,
  • How to explain, visualize, and defend design work,
  • How companies define and track design KPIs,
  • How to make a strong case for UX metrics.

Community Matters ❤️ #

Producing a video course takes quite a bit of time, and we couldn’t pull it off without the support of our wonderful community. So thank you from the bottom of our hearts! We hope you’ll find the course useful for your work. Happy watching, everyone! 🎉🥳

Build A Static RSS Reader To Fight Your Inner FOMO

 RSS is a classic technology that fetches content from websites and feeds it to anyone who subscribes to it with a URL. It’s based on XML, and we can use it to consume the feeds in our own apps. Karin Hendrikse demonstrates how to do exactly that with a static site you can use as your personal RSS reader.

In a fast-paced industry like tech, it can be hard to deal with the fear of missing out on important news. But, as many of us know, there’s an absolutely huge amount of information coming in daily, and finding the right time and balance to keep up can be difficult, if not stressful. A classic piece of technology like an RSS feed is a delightful way of taking back ownership of our own time. In this article, we will create a static Really Simple Syndication (RSS) reader that will bring you the latest curated news only once (yes: once) a day.

We’ll obviously work with RSS technology in the process, but we’re also going to combine it with some things that maybe you haven’t tried before, including Astro (the static site framework), TypeScript (for JavaScript goodies), a package called rss-parser (for connecting things together), as well as scheduled functions and build hooks provided by Netlify (although there are other services that do this).

I chose these technologies purely because I really, really enjoy them! There may be other solutions out there that are more performant, come with more features, or are simply more comfortable to you — and in those cases, I encourage you to swap in whatever you’d like. The most important thing is getting the end result!

The Plan

Here’s how this will go. Astro generates the website. I made the intentional decision to use a static site because I want the different RSS feeds to be fetched only once during build time, and that’s something we can control each time the site is “rebuilt” and redeployed with updates. That’s where Netlify’s scheduled functions come into play, as they let us trigger rebuilds automatically at specific times. There is no need to manually check for updates and deploy them! Cron jobs can just as readily do this if you prefer a server-side solution.

During the triggered rebuild, we’ll let the rss-parser package do exactly what it says it does: parse a list of RSS feeds that are contained in an array. The package also allows us to set a filter for the fetched results so that we only get ones from the past day, week, and so on. Personally, I only render the news from the last seven days to prevent content overload. We’ll get there!

But first…

What Is RSS?

RSS is a web feed technology that you can feed into a reader or news aggregator. Because RSS is standardized, you know what to expect when it comes to the feed’s format. That means we have a ton of fun possibilities when it comes to handling the data that the feed provides. Most news websites have their own RSS feed that you can subscribe to (this is Smashing Magazine’s RSS feed: https://www.smashingmagazine.com/feed/). An RSS feed is capable of updating every time a site publishes new content, which means it can be a quick source of the latest news, but we can tailor that frequency as well.

RSS feeds are written in an Extensible Markup Language (XML) format and have specific elements that can be used within it. Instead of focusing too much on the technicalities here, I’ll give you a link to the RSS specification. Don’t worry; that page should be scannable enough for you to find the most pertinent information you need, like the kinds of elements that are supported and what they represent. For this tutorial, we’re only using the following elements: <title>, <link>, <description>, <item>, and <pubDate>. We’ll also let our RSS parser package do some of the work for us.

Creating The State Site

We’ll start by creating our Astro site! In your terminal run pnpm create astro@latest. You can use any package manager you want — I’m simply trying out pnpm for myself.

After running the command, Astro’s chat-based helper, Houston, walks through some setup questions to get things started.

 astro   Launch sequence initiated.

   dir   Where should we create your new project?
         ./rss-buddy

  tmpl   How would you like to start your new project?
         Include sample files

    ts   Do you plan to write TypeScript?
         Yes

   use   How strict should TypeScript be?
         Strict

  deps   Install dependencies?
         Yes

   git   Initialize a new git repository?
         Yes

I like to use Astro’s sample files so I can get started quickly, but we’re going to clean them up a bit in the process. Let’s clean up the src/pages/index.astro file by removing everything inside of the <main></main> tags. Then we’re good to go!

From there, we can spin things by running pnpm start. Your terminal will tell you which localhost address you can find your site at.

Pulling Information From RSS feeds

The src/pages/index.astro file is where we will make an array of RSS feeds we want to follow. We will be using Astro’s template syntax, so between the two code fences (—), create an array of feedSources and add some feeds. If you need inspiration, you can copy this:

const feedSources = [
  'https://www.smashingmagazine.com/feed/',
  'https://developer.mozilla.org/en-US/blog/rss.xml',
  // etc.
]

Now we’ll install the rss-parser package in our project by running pnpm install rss-parser. This package is a small library that turns the XML that we get from fetching an RSS feed into JavaScript objects. This makes it easy for us to read our RSS feeds and manipulate the data any way we want.

Once the package is installed, open the src/pages/index.astro file, and at the top, we’ll import the rss-parser and instantiate the Partner class.

import Parser from 'rss-parser';
const parser = new Parser();

We use this parser to read our RSS feeds and (surprise!) parse them to JavaScript. We’re going to be dealing with a list of promises here. Normally, I would probably use Promise.all(), but the thing is, this is supposed to be a complicated experience. If one of the feeds doesn’t work for some reason, I’d prefer to simply ignore it.

Why? Well, because Promise.all() rejects everything even if only one of its promises is rejected. That might mean that if one feed doesn’t behave the way I’d expect it to, my entire page would be blank when I grab my hot beverage to read the news in the morning. I do not want to start my day confronted by an error.

Instead, I’ll opt to use Promise.allSettled(). This method will actually let all promises complete even if one of them fails. In our case, this means any feed that errors will just be ignored, which is perfect.

Let’s add this to the src/pages/index.astro file:

interface FeedItem {
  feed?: string;
  title?: string;
  link?: string;
  date?: Date;
}

const feedItems: FeedItem[] = [];

await Promise.allSettled(
  feedSources.map(async (source) => {
    try {
      const feed = await parser.parseURL(source);
      feed.items.forEach((item) => {
        const date = item.pubDate ? new Date(item.pubDate) : undefined;
        
          feedItems.push({
            feed: feed.title,
            title: item.title,
            link: item.link,
            date,
          });
      });
    } catch (error) {
      console.error(`Error fetching feed from ${source}:`, error);
    }
  })
);

This creates an array (or more) named feedItems. For each URL in the feedSources array we created earlier, the rss-parser retrieves the items and, yes, parses them into JavaScript. Then, we return whatever data we want! We’ll keep it simple for now and only return the following:

  • The feed title,
  • The title of the feed item,
  • The link to the item,
  • And the item’s published date.

The next step is to ensure that all items are sorted by date so we’ll truly get the “latest” news. Add this small piece of code to our work:

const sortedFeedItems = feedItems.sort((a, b) => (b.date ?? new Date()).getTime() - (a.date ?? new Date()).getTime());

Oh, and… remember when I said I didn’t want this RSS reader to render anything older than seven days? Let’s tackle that right now since we’re already in this code.

We’ll make a new variable called sevenDaysAgo and assign it a date. We’ll then set that date to seven days ago and use that logic before we add a new item to our feedItems array.

This is what the src/pages/index.astro file should now look like at this point:

---
import Layout from '../layouts/Layout.astro';
import Parser from 'rss-parser';
const parser = new Parser();

const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);

const feedSources = [
  'https://www.smashingmagazine.com/feed/',
  'https://developer.mozilla.org/en-US/blog/rss.xml',
]

interface FeedItem {
  feed?: string;
  title?: string;
  link?: string;
  date?: Date;
}

const feedItems: FeedItem[] = [];

await Promise.allSettled(
  feedSources.map(async (source) => {
    try {
      const feed = await parser.parseURL(source);
      feed.items.forEach((item) => {
        const date = item.pubDate ? new Date(item.pubDate) : undefined;
        if (date && date >= sevenDaysAgo) {
          feedItems.push({
            feed: feed.title,
            title: item.title,
            link: item.link,
            date,
          });
        }
      });
    } catch (error) {
      console.error(`Error fetching feed from ${source}:`, error);
    }
  })
);

const sortedFeedItems = feedItems.sort((a, b) => (b.date ?? new Date()).getTime() - (a.date ?? new Date()).getTime());

---

<Layout title="Welcome to Astro.">
  <main>
  </main>
</Layout>

Rendering XML Data

It’s time to show our news articles on the Astro site! To keep this simple, we’ll format the items in an unordered list rather than some other fancy layout.

All we need to do is update the <Layout> element in the file with the XML objects sprinkled in for a feed item’s title, URL, and publish date.

<Layout title="Welcome to Astro.">
  <main>
  {sortedFeedItems.map(item => (
    <ul>
      <li>
        <a href={item.link}>{item.title}</a>
        <p>{item.feed}</p>
        <p>{item.date}</p>
      </li>
    </ul>
  ))}
  </main>
</Layout>

Go ahead and run pnpm start from the terminal. The page should display an unordered list of feed items. Of course, everything is styled at the moment, but luckily for you, you can make it look exactly like you want with CSS!

And remember that there are even more fields available in the XML for each item if you want to display more information. If you run the following snippet in your DevTools console, you’ll see all of the fields you have at your disposal:

feed.items.forEach(item => {}

Scheduling Daily Static Site Builds

We’re nearly done! The feeds are being fetched, and they are returning data back to us in JavaScript for use in our Astro page template. Since feeds are updated whenever new content is published, we need a way to fetch the latest items from it.

We want to avoid doing any of this manually. So, let’s set this site on Netlify to gain access to their scheduled functions that trigger a rebuild and their build hooks that do the building. Again, other services do this, and you’re welcome to roll this work with another provider — I’m just partial to Netlify since I work there. In any case, you can follow Netlify’s documentation for setting up a new site.

Once your site is hosted and live, you are ready to schedule your rebuilds. A build hook gives you a URL to use to trigger the new build, looking something like this:

https://api.netlify.com/build_hooks/your-build-hook-id

Let’s trigger builds every day at midnight. We’ll use Netlify’s scheduled functions. That’s really why I’m using Netlify to host this in the first place. Having them at the ready via the host greatly simplifies things since there’s no server work or complicated configurations to get this going. Set it and forget it!

We’ll install @netlify/functions (instructions) to the project and then create the following file in the project’s root directory: netlify/functions/deploy.ts.

This is what we want to add to that file:

// netlify/functions/deploy.ts

import type { Config } from '@netlify/functions';

const BUILD_HOOK =
  'https://api.netlify.com/build_hooks/your-build-hook-id'; // replace me!

export default async (req: Request) => {
  await fetch(BUILD_HOOK, {
    method: 'POST',
  })
};

export const config: Config = {
  schedule: '0 0 * * *',
};
If you commit your code and push it, your site should re-deploy automatically. From that point on, it follows a schedule that rebuilds the site every day at midnight, ready for you to take your morning brew and catch up on everything that you think is important.

Interview With Björn Ottosson, Creator Of The Oklab Color Space

 Go behind the scenes with Björn Ottosson, the Swedish engineer who created Oklab color space, and discover how he developed a simple yet effective model with good hue uniformity while also handling lightness and saturation well — and is “okay” to use.

Oklab is a new perceptual color space supported in all major browsers created by the Swedish engineer Björn Ottosson. In this interview, Philip Jägenstedt explores how and why Björn created Oklab and how it spread across the ecosystem.

Note: The original interview was conducted in Swedish and is available to watch.

About Björn

Philip Jägenstedt: Tell me a little about yourself, Björn.

Photo of Björn OttossonBjörn Ottosson: I worked for many years in the game industry on game engines and games like FIFA, Battlefield, and Need for Speed. I’ve always been interested in technology and its interaction with the arts. I’m an engineer, but I’ve always held both of these interests.

On Working With Color

Philip: For someone who hasn’t dug into colors much, what’s so hard about working with them?

Björn: Intuitively, colors can seem quite simple. A color can be lighter or darker, it can be more blue or more green, and so on. Everyone with typical color vision has a fairly similar experience of color, and this can be modeled.

However, the way we manipulate colors in software usually doesn’t align with human perception of colors. The most common color space is sRGB. There’s also HSL, which is common for choosing colors, but it’s also based on sRGB.

One problem with sRGB is that in a gradient between blue and white, it becomes a bit purple in the middle of the transition. That’s because sRGB really isn’t created to mimic how the eye sees colors; rather, it is based on how CRT monitors work. That means it works with certain frequencies of red, green, and blue, and also the non-linear coding called gamma. It’s a miracle it works as well as it does, but it’s not connected to color perception. When using those tools, you sometimes get surprising results, like purple in the gradient.

Purple in the gradient
Image source: How software gets color wrong. (Large preview)

On Color Perception

Philip: How do humans perceive color?

Björn: When light enters the eye and hits the retina, it’s processed in many layers of neurons and creates a mental impression. It’s unlikely that the process would be simple and linear, and it’s not. But incredibly enough, most people still perceive colors similarly.

People have been trying to understand colors and have created color wheels and similar visualizations for hundreds of years. During the 20th century, a lot of research and modeling went into color vision. For example, the CIE XYZ model is based on how sensitive our photoreceptor cells are to different frequencies of light. CIE XYZ is still a foundational color space on which all other color spaces are based.

There were also attempts to create simple models matching human perception based on XYZ, but as it turned out, it’s not possible to model all color vision that way. Perception of color is incredibly complex and depends, among other things, on whether it is dark or light in the room and the background color it is against. When you look at a photograph, it also depends on what you think the color of the light source is. The dress is a typical example of color vision being very context-dependent. It is almost impossible to model this perfectly.

Models that try to take all of this complexity into account are called color appearance models. Although they have many applications, they’re not that useful if you don’t know if the viewer is in a light or bright room or other viewing conditions.

The odd thing is that there’s a gap between the tools we typically use — such as sRGB and HSL — and the findings of this much older research. To an extent, this makes sense because when HSL was developed in the 1970s, we didn’t have much computing power, so it’s a fairly simple translation of RGB. However, not much has changed since then.

We have a lot more processing power now, but we’ve settled for fairly simple tools for handling colors in software.

Display technology has also improved. Many displays now have different RGB primaries, i.e., a redder red, greener green, or bluer blue. sRGB cannot reach all colors available on these displays. The new P3 color space can, but it’s very similar to sRGB, just a little wider.

On Creating Oklab

Philip: What, then, is Oklab, and how did you create it?

Björn: When working in the game industry, sometimes I wanted to do simple color manipulations like making a color darker or changing the hue. I researched existing color spaces and how good they are at these simple tasks and concluded that all of them are problematic in some way.

Many people know about CIE Lab. It’s quite close to human perception of color, but the handling of hue is not great. For example, a gradient between blue and white turns out purple in CIE Lab, similar to in sRGB. Some color spaces handle hue well but have other issues to consider.

When I left my job in gaming to pursue education and consulting, I had a bit of time to tackle this problem. Oklab is my attempt to find a better balance, something Lab-like but “okay”.

I based Oklab on two other color spaces, CIECAM16 and IPT. I used the lightness and saturation prediction from CIECAM16, which is a color appearance model, as a target. I actually wanted to use the datasets used to create CIECAM16, but I couldn’t find them.

IPT was designed to have better hue uniformity. In experiments, they asked people to match light and dark colors, saturated and unsaturated colors, which resulted in a dataset for which colors, subjectively, have the same hue. IPT has a few other issues but is the basis for hue in Oklab.

Using these three datasets, I set out to create a simple color space that would be “okay”. I used an approach quite similar to IPT but combined it with the lightness and saturation estimates from CIECAM16. The resulting Oklab still has good hue uniformity but also handles lightness and saturation well.

Philip: How about the name Oklab? Why is it just okay?

Björn: This is a bit tongue-in-cheek and some amount of humility.

For the tasks I had in mind, existing color spaces weren’t okay, and my goal was to make one that is. At the same time, it is possible to delve deeper. If a university had worked on this, they could have run studies with many participants. For a color space intended mainly for use on computer and phone screens, you could run studies in typical environments where they are used. It’s possible to go deeper.

Nevertheless, I took the datasets I could find and made the best of what I had. The objective was to make a very simple model that’s okay to use. And I think it is okay, and I couldn’t come up with anything better. I didn’t want to call it Björn Ottosson Lab or something like that, so I went with Oklab.

Philip: Does the name follow a tradition of calling things okay? I know there’s also a Quite OK Image format.

Björn: No, I didn’t follow any tradition here. Oklab was just the name I came up with.

On Oklab Adoption

Philip: I discovered Oklab when it suddenly appeared in all browsers. Things often move slowly on the web, but in this case, things moved very quickly. How did it happen?

Björn: I was surprised, too! I wrote a blog post and shared it on Twitter.

I have a lot of contacts in the gaming industry and some contacts in the Visual Effects (VFX) industry. I expected that people working with shaders or visual effects might try this out, and maybe it would be used in some games, perhaps as an effect for a smooth color transition.

But the blog post was spread much more widely than I thought. It was on Hacker News, and many people read it.

The code for Oklab is only 10 lines long, so many open-source libraries have adopted it. This all happened very quickly.

Chris Lilley from the W3C got in touch and asked me some questions about Oklab. We discussed it a bit, and I explained how it works and why I created it. He gave a presentation at a conference about it, and then he pushed for it to be added to CSS.

Photoshop also changed its gradients to use Oklab. All of this happened organically without me having to cheer it on.

On Okhsl #

Philip: In another blog post, you introduced two other color spaces, Okhsv and Okhsl. You’ve already talked about HSL, so what is Okhsl?

Björn: When picking colors, HSL has a big advantage, which is that the parameter space is simple. Any value 0-360 for hue (H) together with any values 0-1 for saturation (S) and lightness (L) are valid combinations and result in different colors on screen. The geometry of HSL is a cylinder, and there’s no way to end up outside that cylinder accidentally.

Color solid cylinder
Image source: Wikipedia. (Large preview)

By contrast, Oklab contains all physically possible colors, but there are combinations of values that don’t work where you reach colors that don’t exist. For example, starting from light and saturated yellow in Oklab and rotating the hue to blue, that blue color does not exist in sRGB; there are only darker and less saturated blues. That’s because sRGB in Oklab has a strange shape, so it’s easy to end up going outside it. This makes it difficult to select and manipulate colors with Oklab or Oklch.

sRGB shape in Oklab.
sRGB shape in Oklab. (Image source: Chris Cameron demo) (Large preview)

Okhsl was an attempt at compromise. It maintains Oklab’s behavior for colors that are not very saturated, close to gray, and beyond that, stretches out to a cylinder that contains all of sRGB. Another way to put it is that the strange shape of sRGB in Oklab has been stretched into a cylinder with reasonably smooth transitions.

The result is similar to HSL, where all parameters can be changed independently without ending up outside sRGB. It also makes Okhsl more complicated than Oklab. There are unavoidable compromises to get something with the characteristics that HSL has.

Everything with color is about compromises. Color vision is so complex that it's about making practical compromises.

This is an area where I wish there were more research. If I have a white background and want to pick some nice colors to put on it, then you can make a lot of assumptions. Okhsl solves many things, but is it possible to do even better?


On Color Compromises

Philip: Some people who have tried Oklab say there are too many dark shades. You changed that in Okhsl with a new lightness estimate.

Björn: This is because Oklab is exposure invariant and doesn’t account for viewing conditions, such as the background color. On the web, there’s usually a white background, which makes it harder to see the difference between black and other dark colors. But if you look at the same gradient on a black background, the difference is more apparent.

CIE Lab handles this, and I tried to handle it in Okhsl, too. So, gradients in Okhsl look better on a white background, but there will be other issues on a black background. It’s always a compromise.

And, Finally…

Philip: Final question: What’s your favorite color?

Björn: I would have to say Burgundy. Burgundy, dark greens, and navy blues are favorites.

Philip: Thank you for your time, Björn. I hope our readers have learned something, and I’ll remind them of your excellent blog, where you go into more depth about Oklab and Okhsl.

Björn: Thank you!