<?xml version="1.0" encoding="utf-8" standalone="yes"?><?xml-stylesheet href="/rss.xsl" type="text/xsl"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"><title>Email on Hyteck</title><link href="https://hyteck.de/tags/email/"/><generator>Hugo -- gohugo.io</generator><language>en-us</language><id>https://hyteck.de/tags/email/</id><updated>2025-07-12T12:05:10+02:00</updated><link href="https://hyteck.de/tags/email/index.xml" rel="self" type="application/rss+xml"/><entry><title>Thoughts on HTML mails</title><link href="https://hyteck.de/post/about-html-mails/" type="application/octet-stream"/><updated>2025-07-12T12:05:10+02:00</updated><id>https://hyteck.de/post/about-html-mails/</id><author><name>Julian-Samuel Gebühr</name></author><content type="html"> &lt;p>Lately I worked on notification e-mails for &lt;a href="https://notfellchen.org">notfellchen.org&lt;/a>. Initially I just sent text
notifications without links to the site. Terrible idea! An E-Mail notification I send always has Call-to-Action or at
minimum a link to more information.&lt;/p>
&lt;p>I left the system like this for half a year because it kinda worked for me (didn&amp;rsquo;t suck enough for me to care), and I was the main receiver of these notifications.
However, as the platform is developed further and more users join I need to think about more user-centric notifications.&lt;/p>
&lt;p>So what do I imagine is important to a user?
*&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Information benefit&lt;/strong>: An e-mail has the purpose to inform a user. This information should be immediately visible &amp;amp; understandable.&lt;/li>
&lt;li>&lt;strong>Actionables&lt;/strong>: Users should be able to act on the information received. This is the bright red button &amp;ldquo;DO SOMETHING NOW!&amp;rdquo; you see so often.&lt;/li>
&lt;li>&lt;strong>Unsubscribing&lt;/strong>: Informing e-mails stop is not only a legal requirement and morally the right thing to do but it also gives users agency and - I hope - increases the User Experience&lt;/li>
&lt;/ul>
&lt;p>With these I naturally came to the next question: Plaintext or HTML?&lt;/p>
&lt;p>Some people would say &lt;a href="https://useplaintext.email/">Plaintext is inherently better&lt;/a> than HTML e-mails. Many of these reasons resonate with me including:&lt;/p>
&lt;ul>
&lt;li>Privacy invasion and tracking&lt;/li>
&lt;li>HTML emails are less accessible&lt;/li>
&lt;li>Some clients can&amp;rsquo;t display HTML emails at all&lt;/li>
&lt;li>Mail client vulnerabilities&lt;/li>
&lt;/ul>
&lt;p>These are all valid points and are a reason I generally enjoy plaintext e-mails when I receive them.
But this is not about me but users. And there are some real benefits of HTML e-mails:&lt;/p>
&lt;ul>
&lt;li>Visually appealing: This is subjective but generally most users seem to agree on that&lt;/li>
&lt;li>User guidance: Rich text provides a real benefit when searching for the relevant information&lt;/li>
&lt;/ul>
&lt;p>Be honest: Do you read automated e-mails you receive completely? Or do you just skim for important information?&lt;/p>
&lt;p>And here HTML-mails shine: &lt;strong>Information can easily be highlighted&lt;/strong> and big button can lead the user to do the right action.
Some might argue that you can also a highlight a link in plaintext but that nearly always will worsen accessibility for screen-reader user.&lt;/p>
&lt;h1 id="the-result">The result&lt;/h1>
&lt;p>In the end, I decided that providing plaintext-only e-mails was not enough. I set up html mails, mostly using
&lt;a href="https://docs.djangoproject.com/en/5.2/topics/email/#send-mail">djangos send_mail&lt;/a> function where I can pass the html message and attattching it correctly is done for me.&lt;/p>
&lt;p>&lt;img src="mail_screenshot.png" alt="A screenshot of an e-mail in thunderbird. The e-mail is structured in header, body and footer. The header says &amp;ldquo;Notfellchen.org&amp;rdquo;, the body shows a message that a new user was registered and a bright green button to show the user. The footer offers a link to unsubscribe">&lt;/p>
&lt;p>For anyone that is interested, here is how most my notifications are sent&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">send_notification_email&lt;/span>(notification_pk):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> notification &lt;span style="color:#f92672">=&lt;/span> Notification&lt;span style="color:#f92672">.&lt;/span>objects&lt;span style="color:#f92672">.&lt;/span>get(pk&lt;span style="color:#f92672">=&lt;/span>notification_pk)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> subject &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">f&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">{&lt;/span>notification&lt;span style="color:#f92672">.&lt;/span>title&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context &lt;span style="color:#f92672">=&lt;/span> {&lt;span style="color:#e6db74">&amp;#34;notification&amp;#34;&lt;/span>: notification, }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> notification&lt;span style="color:#f92672">.&lt;/span>notification_type &lt;span style="color:#f92672">==&lt;/span> NotificationTypeChoices&lt;span style="color:#f92672">.&lt;/span>NEW_REPORT_COMMENT &lt;span style="color:#f92672">or&lt;/span> notification&lt;span style="color:#f92672">.&lt;/span>notification_type &lt;span style="color:#f92672">==&lt;/span> NotificationTypeChoices&lt;span style="color:#f92672">.&lt;/span>NEW_REPORT_AN:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> html_message &lt;span style="color:#f92672">=&lt;/span> render_to_string(&lt;span style="color:#e6db74">&amp;#39;fellchensammlung/mail/notifications/report.html&amp;#39;&lt;/span>, context)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> plain_message &lt;span style="color:#f92672">=&lt;/span> render_to_string(&lt;span style="color:#e6db74">&amp;#39;fellchensammlung/mail/notifications/report.txt&amp;#39;&lt;/span>, context)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> [&lt;span style="color:#f92672">...&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">elif&lt;/span> notification&lt;span style="color:#f92672">.&lt;/span>notification_type &lt;span style="color:#f92672">==&lt;/span> NotificationTypeChoices&lt;span style="color:#f92672">.&lt;/span>NEW_COMMENT:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> html_message &lt;span style="color:#f92672">=&lt;/span> render_to_string(&lt;span style="color:#e6db74">&amp;#39;fellchensammlung/mail/notifications/new-comment.html&amp;#39;&lt;/span>, context)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> plain_message &lt;span style="color:#f92672">=&lt;/span> render_to_string(&lt;span style="color:#e6db74">&amp;#39;fellchensammlung/mail/notifications/new-comment.txt&amp;#39;&lt;/span>, context)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">else&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">raise&lt;/span> &lt;span style="color:#a6e22e">NotImplementedError&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Unknown notification type&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> &lt;span style="color:#e6db74">&amp;#34;plain_message&amp;#34;&lt;/span> &lt;span style="color:#f92672">not&lt;/span> &lt;span style="color:#f92672">in&lt;/span> locals():
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> plain_message &lt;span style="color:#f92672">=&lt;/span> strip_tags(html_message)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mail&lt;span style="color:#f92672">.&lt;/span>send_mail(subject, plain_message, settings&lt;span style="color:#f92672">.&lt;/span>DEFAULT_FROM_EMAIL,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> [notification&lt;span style="color:#f92672">.&lt;/span>user_to_notify&lt;span style="color:#f92672">.&lt;/span>email],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> html_message&lt;span style="color:#f92672">=&lt;/span>html_message)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Yes this could be made more efficient - for now it works. I made the notification framework too complicated initially, so I&amp;rsquo;m still tyring out what works and what doesn&amp;rsquo;t.&lt;/p>
&lt;p>Here is the html template&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-html" data-lang="html">&lt;span style="display:flex;">&lt;span>{% extends &amp;#34;fellchensammlung/mail/base.html&amp;#34; %}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{% load i18n %}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{% block title %}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {% translate &amp;#39;Neuer User&amp;#39; %}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{% endblock %}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{% block content %}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#f92672">p&lt;/span>&amp;gt;Moin,&amp;lt;/&lt;span style="color:#f92672">p&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#f92672">p&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> es wurde ein neuer Useraccount erstellt.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;/&lt;span style="color:#f92672">p&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#f92672">p&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Details findest du hier
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;/&lt;span style="color:#f92672">p&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#f92672">p&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#f92672">a&lt;/span> &lt;span style="color:#a6e22e">href&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;{{ notification.user_related.get_full_url }}&amp;#34;&lt;/span> &lt;span style="color:#a6e22e">class&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;cta-button&amp;#34;&lt;/span>&amp;gt;{% translate &amp;#39;User anzeigen&amp;#39; %}&amp;lt;/&lt;span style="color:#f92672">a&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;/&lt;span style="color:#f92672">p&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{% endblock %}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>and here the plaintext&lt;/p>
&lt;pre tabindex="0">&lt;code>{% extends &amp;#34;fellchensammlung/mail/base.txt&amp;#34; %}
{% load i18n %}
{% block content %}{% blocktranslate %}Moin,
es wurde ein neuer Useraccount erstellt.
User anzeigen: {{ new_user_url }}
{% endblocktranslate %}{% endblock %}
&lt;/code>&lt;/pre>&lt;p>Works pretty well for now. People that prefer plaintext will get these and most users will have skimmable html e-mail where the
styling will help them recognize where it&amp;rsquo;s from and what to do. Accessibility-wise this seems like the best option.&lt;/p>
&lt;p>And while adding a new notification will force me to create&lt;/p>
&lt;ul>
&lt;li>a new notification type,&lt;/li>
&lt;li>two new e-mail templates and&lt;/li>
&lt;li>a proper rendering on the website&lt;/li>
&lt;/ul>
&lt;p>this seems okay. Notifications are useful, but I don&amp;rsquo;t want to shove them everywhere. I&amp;rsquo;m not running facebook or linkedin after all.&lt;/p>
&lt;p>So for now I&amp;rsquo;m pretty happy with the new shiny e-mails and will roll out the changes soon (if I don&amp;rsquo;t find any more wired bugs).&lt;/p>
&lt;p>PS: I wrote this post after reading &lt;a href="https://blog.avas.space/blog-website-eval/">blog &amp;amp; website in the age of containerized socials&lt;/a> by ava.
Maybe this &amp;ldquo;Thoughts on&amp;rdquo; format will stay and I will post these in addition to more structured deep dives.&lt;/p>
&lt;h1 id="update">Update&lt;/h1>
&lt;p>I did a rework of the notification function and it&amp;rsquo;s now much cleaner now. However, it&amp;rsquo;s less readable so this blogpost will stay as-is.
If you want to check out the new code have a look &lt;a href="https://codeberg.org/moanos/notfellchen/src/commit/a4b8486bd489dacf8867b49d04f70f091556dc9d/src/fellchensammlung/mail.py">on Codeberg&lt;/a>.&lt;/p></content></entry></feed>