<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:wfw="http://wellformedweb.org/CommentAPI/"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
     xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
     xmlns:georss="http://www.georss.org/georss"
     xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
     xmlns:media="http://search.yahoo.com/mrss/">
  <channel>
    <title>rtzptz</title>
    <atom:link
      href="https://ionos.rtzptz.xyz/feed.xml"
      rel="self" type="application/rss+xml" />
    <link>https://ionos.rtzptz.xyz/</link>
    <description><![CDATA[]]></description>
    <language>en</language>
    <pubDate>Wed, 18 Feb 2026 00:00:00 +0000</pubDate>
    <lastBuildDate>Fri, 06 Mar 2026 17:13:28 +0000</lastBuildDate>
    <generator>weblorg 0.1.0 (https://emacs.love/weblorg)</generator>
    <webMaster>ratzeputz@rtzptz.xyz</webMaster>
    <image>
      <url>https://ionos.rtzptz.xyz/media/img/8bitme.png</url>
      <title>Blog Author</title>
      <link>https://ionos.rtzptz.xyz/</link>
    </image>

    
    <item>
      <title>Picocss</title>
      <link>https://ionos.rtzptz.xyz/posts/website-picocss.html</link>
      <author>author@mail.com (Blog Author)</author>
      <guid isPermaLink="false">https://ionos.rtzptz.xyz/posts/website-picocss.html</guid>
      
        <pubDate>Wed, 18 Feb 2026 00:00:00 +0000</pubDate>
      
      <description><![CDATA[<div id="table-of-contents" role="doc-toc">
<h2>Table of Contents</h2>
<div id="text-table-of-contents" role="doc-toc">
<ul>
<li><a href="#integration-into-a-website">1. Integration into a website</a></li>
</ul>
</div>
</div>
<p>
As a developer, I sometimes build small websites to demonstrate something or simply
provide content. For my purpose, a minimal CSS framework offering basic styling
is sufficient. Every time I start again, I search for a framework that looks
good enough and start building. However, as I don't use the same framework across projects,
it is difficult to maintain.
</p>

<p>
So, I took some time to look at which minimal framework I should use for my next projects
or to rewrite older ones. I decided on <a href="https://picocss.com/docs">picocss</a>.
</p>

<p>
It comes in to version: a classless version with minimal functionality and
components and a class version with more components and styling options.
It offers optional color palettes and support for dark and light mode.
</p>

<p>
The "killer" feature is that <i>picocss</i> is actively maintained which is not the norm.
</p>
<div id="outline-container-integration-into-a-website" class="outline-2">
<h2 id="integration-into-a-website"><span class="section-number-2">1.</span> Integration into a website</h2>
<div class="outline-text-2" id="text-1">
<p>
Add the stylesheets for the classless version and the color palette to the header. The
Google <i>Noto</i> font is used as the font of choice.
</p>

<div class="org-src-container">
<pre class="src src-html"><code>&lt;link
  rel="stylesheet"
  href="https://fonts.googleapis.com/css2?family=Noto+Serif:ital,wght@0,100..900;1,100..900&amp;display=swap" &gt;
&lt;link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.min.css"&gt;
&lt;link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.colors.min.css"&gt;
</code></pre>
</div>

<p>
The framework applies directly to semantic HTML tags.
</p>

<p>
As CSS has gradually improved over time, most of the styling and some dynamic content
can now be achieved using only CSS, without the need for Javascript or heavy frameworks.
</p>

<p>
For most layouts, <a href="https://css-tricks.com/snippets/css/a-guide-to-flexbox/">flexbox</a> is sufficient.
</p>

<p>
The website <a href="https://modern-css.com/">modern css</a> lists a vast amount of examples where CSS can used more quickly and
clean. <a href="https://www.30secondsofcode.org/css/p/1/">30secondsofcode</a> and
<a href="https://www.magicpattern.design/tools/css-backgrounds">CSS backgrounds</a> offer ideas for helpful and stylistic elements that can be achieved using
CSS alone.
</p>


<p>
Although these notes mostly explain my rational for choosing <i>picocss</i>, please feel free to
suggest improvements.
</p>
</div>
</div>
]]></description>
    </item>
    
    <item>
      <title>Trying weblorg for blogging and my personal site</title>
      <link>https://ionos.rtzptz.xyz/posts/hello-weblorg.html</link>
      <author>author@mail.com (Blog Author)</author>
      <guid isPermaLink="false">https://ionos.rtzptz.xyz/posts/hello-weblorg.html</guid>
      
        <pubDate>Sat, 14 Feb 2026 00:00:00 +0000</pubDate>
      
      <description><![CDATA[<div id="table-of-contents" role="doc-toc">
<h2>Table of Contents</h2>
<div id="text-table-of-contents" role="doc-toc">
<ul>
<li><a href="#introduction">1. Introduction</a></li>
<li><a href="#setup-weblorg">2. Setup weblorg</a></li>
<li><a href="#useful-helpers">3. Useful helpers</a></li>
<li><a href="#cd-with-codeberg">4. CD with codeberg</a></li>
</ul>
</div>
</div>
<p>
Time to start my blog for the third time. The first two times I got stuck
either through complex workflows or I didn't get used to write for myself.
</p>

<p>
As I think there are several benefits of writing stuff down, I give my blog a revive.
</p>
<div id="outline-container-introduction" class="outline-2">
<h2 id="introduction"><span class="section-number-2">1.</span> Introduction</h2>
<div class="outline-text-2" id="text-1">
<p>
The last time I tried <a href="https://gohugo.io/">hugo</a> together with
<a href="https://ox-hugo.scripter.co/">ox-hugo</a> and <a href="https://github.com/masasam/emacs-easy-hugo">easy-hugo</a>. The setup was fairly easy but required working with
different programs and abstractions and I didn't automate the publishing part.
</p>

<p>
I think these obstacles are the major reason that the blogging doesn't work well for
myself. This time I wanted a better integration into <code>org mode</code> and <code>Emacs</code> and publishing
workflow which is done by some CI.
</p>

<p>
This time I gave <a href="https://emacs.love/weblorg/">weblorg</a> a try. The journey was a mixed experience and I think the setup
process should deserve some explanation, to help other struggling with getting used to
<code>weblorg</code>.
</p>
</div>
</div>
<div id="outline-container-setup-weblorg" class="outline-2">
<h2 id="setup-weblorg"><span class="section-number-2">2.</span> Setup weblorg</h2>
<div class="outline-text-2" id="text-2">
<p>
The setup was straight forward following the official <a href="https://emacs.love/weblorg/doc/">documentation</a>.
</p>

<p>
I started with a minimal example and created the directory structure as described by the
<code>weblorg</code> documentation:
</p>

<pre class="example" id="orgef503a2">
blog/
|- posts/
|- pages/
|- publish.el
</pre>

<p>
the structure is fairly simple as the <code>posts</code> folder contains the blog posts and the <code>pages</code>
folder all other pages.
</p>

<p>
The <code>publish.el</code> file serves as definition and export script. I followed closely the example
from the documentation and only added my own template variables.
</p>

<p>
The first version of my <code>publish.el</code>:
</p>

<div class="org-src-container">
<pre class="src src-emacs-lisp"><code>  (require 'org)
  (require 'weblorg)

  (weblorg-site
   :template-vars '(("site_name" . "rtzptz")
                    ("site_description" . "puzzling void")
                    ("site_owner" . "ratzeputz@rtzptz.xyz")))

(weblorg-route
 :name "posts"
 :input-pattern "posts/*.org"
 :template "post.html"
 :output "output/posts/{{ slug }}.html"
 :url "/posts/{{ slug }}.html")

;; route for rendering the index page of the blog
(weblorg-route
 :name "blog"
 :input-pattern "posts/*.org"
 :input-aggregate #'weblorg-input-aggregate-all-desc
 :template "blog.html"
 :output "output/index.html"
 :url "/")

;; route for rendering each page
(weblorg-route
 :name "pages"
 :input-pattern "pages/*.org"
 :template "page.html"
 :output "output/{{ slug }}.html"
 :url "/{{ slug }}.html")

;; route for static assets that also copies files to output directory
(weblorg-copy-static
 :output "static/{{ file }}"
 :url "/static/{{ file }}")

;; fire the engine and export all the files declared in the routes above
(weblorg-export)
</code></pre>
</div>

<p>
As one can see, <code>weblorg</code> uses route definitions for matching input files with templates.
Running <code>(weblorg-export)</code> at the end generates the defined output HTML files.
</p>

<p>
The first export works pretty well but I stumbled that not all pages I defined got
generated. As the template engine and the default template are not obvious present in the
documentation, I tinkered a while before I dove into the source code.
</p>

<p>
The default template only supports the page <code>about</code> and no other pages.
Supporting arbitrary pages and dumping the default template would greatly improve the
first-user experience. I think its natural that every user rolls it own template and thus
the default should be easily accessible from inside Emacs.
To start with out own template, see the <a href="https://github.com/emacs-love/weblorg-template">weblorg-template</a> repository and <a href="https://github.com/emacs-love/templatel">templatel</a>. Note
that the templating engine only supports a subset of the <a href="https://jinja.palletsprojects.com/">jinja</a> templating specification.
</p>
</div>
</div>
<div id="outline-container-useful-helpers" class="outline-2">
<h2 id="useful-helpers"><span class="section-number-2">3.</span> Useful helpers</h2>
<div class="outline-text-2" id="text-3">
<p>
<code>weblorg</code> comes with everything in-place to get started but doesn't offer some
quality-of-life functions for writing posts or running the export. Well it's Emacs just
roll out your own.
</p>

<div class="org-src-container">
<pre class="src src-emacs-lisp"><code>;; A simple http server which is used upon site generation
(use-package simple-httpd
  :ensure t)

  ;; Define blog constants
  (defconst jj-site-path
    (expand-file-name "~/repos/rtzptz-site/"))
  (defconst jj-site-posts-path (expand-file-name "posts" jj-site-path))
  (defconst jj-site-local-dev-p t)
  (defconst jj-time-format "%Y-%m-%d")

;; Helper functions to quickly access the blog content or files
  (defun jj-site-open-file (file-name)
    "Ask the user which blog file to open."
    (interactive (list (read-file-name "File:" jj-site-path)))
    (find-file file-name))

  (defun jj-site-open-post (file-name)
    "Ask the user which blog post should be opened?"
    (interactive
     (list (read-file-name "Post:"
                           (expand-file-name "posts/" jj-site-path))))
    (find-file file-name))

;; Generate the blog and start a local webserver
(defun jj-site-generate ()
  "Evaluate the publish.el file in the website path."
  (interactive)
  (let* ((publish "publish.el")
         (old-default-directory default-directory))
    (save-excursion
      (delete-directory (expand-file-name "output" jj-site-path) t)
      (find-file-noselect (expand-file-name publish jj-site-path))
      (setq-local default-directory jj-site-path)
       (eval-buffer publish)
       (setq-local default-directory old-default-directory)))
  (unless (httpd-running-p)
    (setq httpd-root (expand-file-name "output" jj-site-path))
    (httpd-start)))

;; Generate a new blog post with the file format &lt;Y-m-d&gt;-&lt;name&gt;
(defun jj-site--post-template ()
  (mapconcat #'identity
             '("#+TITLE: %s\n"
               "#+SLUG:\n"
               "#+DATE:\n"
               "#+DRAFT: t\n"
               "#+FILETAGS:\n"
               "\n"
               "Write something")))

(defun jj-org--slugify-name (name)
  (let* ((s (downcase name))
         (s (replace-regexp-in-string "[[:space:]]+" "-" s)))
    (if (string-empty-p s) "untitled" s)))

(defun jj-site-create-post (post-title)
  "Creates a new blog post from a template."
  (interactive (list (read-string "Post name:")))
  (let* ((slug (jj-org--slugify-name post-title))
         (file-name (concat (format-time-string jj-time-format (current-time))
                            "-" slug ".org"))
         (file-path (expand-file-name file-name jj-site-posts-path)))
    (message "%s" file-path)
    (unless (file-exists-p file-path)
      (with-temp-file file-path
        (insert (format (jj-site--post-template) post-title))
        (unless (bolp) (insert "\n"))))
    (find-file file-path)
    (goto-char (point-min))
    (end-of-line)))
</code></pre>
</div>

<p>
With these helper function I can quickly open my blog posts, view the blog on my local
machine and create new post files. For the moment this is enough to get me working.
</p>

<p>
The <a href="https://github.com/skeeto/emacs-web-server/blob/master/simple-httpd.el">simple-httpd</a> server starts at <a href="http://localhost:8080/">http://localhost:8080/</a> and runs as long as either Emacs
is running or it is killed with <code>(httpd-stop)</code>.
</p>
</div>
</div>
<div id="outline-container-cd-with-codeberg" class="outline-2">
<h2 id="cd-with-codeberg"><span class="section-number-2">4.</span> CD with codeberg</h2>
<div class="outline-text-2" id="text-4">
<p>
The last part is to setup a CD workflow. I want to push the latest changes to a git
repository at <a href="https://codeberg.org/">Codeberg</a> and let the CD handle the generation of the site and how it gets
published on my server.
</p>

<p>
One note: Codebarg uses the <a href="https://woodpecker-ci.org/">Woodpecker</a> CI and every project needs to get a manual
approve and the project needs to be public. See <a href="https://codeberg.org/Codeberg-e.V./requests">this repository</a> and <a href="https://codeberg.org/Codeberg-CI">this one</a> for the
process. The access is typically granted within a day and one can managed it under
<a href="https://ci.codeberg.org/repos">ci.codeberg.org</a>.
</p>

<p>
If you have experience with Github Actions, the Woodpecker CI feels quite familiar. The
deployment is straight forward and I noticed only one main difference. Woodpecker allows a
new docker image for every step which uses the same persistent storage in the background.
</p>

<div class="org-src-container">
<pre class="src src-yaml"><code>  when:
  - event: push
    branch: main
  - event: manual

steps:
  - name: build
    image: debian:trixie-slim
    commands:
      - apt-get update
      - apt-get install -y --no-install-recommends emacs-nox git ca-certificates
      - emacs --batch --script publish.el

  - name: deploy
    image: alpine:3.20
    commands:
      - apk add --no-cache openssh-client rsync
      - mkdir -p ~/.ssh
      - printf "%s\n" "$${SSH_KEY}" &gt; ~/.ssh/id_ed25519
      - chmod 600 ~/.ssh/id_ed25519
      # besser: known_hosts als Secret pflegen; ssh-keyscan ist pragmatisch
      - ssh-keyscan -H $$DEPLOY_HOST &gt;&gt; ~/.ssh/known_hosts
      - rsync -avz --delete output/ "$${DEPLOY_USER}@$${DEPLOY_HOST}:$${DEPLOY_PATH}/"
    environment:
      DEPLOY_HOST:
        from_secret: deploy_host
      DEPLOY_USER:
        from_secret: deploy_user
      DEPLOY_PATH:
        from_secret: deploy_path
      SSH_KEY:
        from_secret: deploy_ssh_key
</code></pre>
</div>

<p>
As everything was in place and I'm very happy to get the whole stuff done in an evening, I
only saw failed CI actions. It took me a while as the Emacs and <code>weblorg</code> trace where not
really readable (<code>weblorg</code> needs better error messages), that the org version provided by
the CI Emacs version doesn't fit to the <code>weblorg</code> expectations. Thus, I needed to adjust the
<code>publish.el</code> script to use the latest org version.
</p>

<p>
This is the adjusted script file:
</p>

<div class="org-src-container">
<pre class="src src-emacs-lisp"><code>  ;; ;; Setup package archive for org and weblorg
(setq package-archives
      '(("gnu"    . "https://elpa.gnu.org/packages/")
        ("nongnu" . "https://elpa.nongnu.org/nongnu/")
        ("melpa"  . "https://melpa.org/packages/")))

(package-initialize)

(unless package-archive-contents
  (package-refresh-contents))

(when (boundp 'package-install-upgrade-built-in)
  (setq package-install-upgrade-built-in t))

(package-install 'org)
(message "OK: Org loaded: %s  (%s)" (org-version) (locate-library "org"))

(unless (package-installed-p 'weblorg)
  (package-refresh-contents)
  (package-install 'weblorg t))

(require 'weblorg)

;; Rest of the file
</code></pre>
</div>

<p>
Now it works stable across the CI and my local machine.
</p>

<p>
To sum this up:
</p>
<ul class="org-ul">
<li>The <code>weblorg</code> user experience is fine but some QoL functions can greatly improve the
experience.</li>
<li>The setup is fairly simple and works for CD deployment.</li>
<li>The external programs I used prior to this setup often got in my way and this pure Emacs
setup feels more naturally.</li>
</ul>

<p>
Feel free to write me and suggest improvements or for general discussions.
</p>
</div>
</div>
]]></description>
    </item>
    

  </channel>
</rss>
