Changes in Blogging Setup
[TL;DR] A few things bothered me in my blogging setup, here's a summary of what I changed.
9 | And: I still have no idea about web development ¯\_(ツ)_/¯
10 |
11 |
12 |
Since I published my blog for the first time about a year ago, quite a few things
13 | changed in my setup:
14 | gradually I added things I was convinced I need (for example functions to generate
15 | RSS), and soon I had a ca. 800 lines of code big elisp tumor to simply...
16 | generate a static website.
17 |
18 |
19 |
So it was time for some cleaning and re-structuring:
20 |
21 |
22 |
23 |
- I kicked out the RSS functions (sorry not sorry to the 3 people who asked
24 | for RSS)
25 |
26 | - I kicked out the functions to create a page with links (why did I even think
27 | that makes sense in the first place...?)
28 |
29 | - I switched to ox-slimhtml
30 |
31 | - I added a theme-switcher to switch between a light or a dark theme
32 |
33 | - some other minor changes: I changed the folder structure, changed and
34 | added some templates, changed the format of archive items
35 |
36 |
37 |
And some things that won't change:
38 |
39 |
40 |
41 |
- this website still doesn't use JavaScript
42 |
43 | - it's still generated with Emacs and org-mode
44 |
45 | - there's still no tracker or other stuff that would
46 | disrespect your privacy
47 |
48 | - I still have no idea about html and CSS...
49 |
50 |
51 |
52 |
53 |
And here are some details about some of the changes:
54 |
55 |
56 |
Switch themes with CSS and html only (no JavaScript)
Spoiler: Because of my lack of CSS and html knowledge (and my stubborness not
57 | to use JavaScript) this was a huge PITA.
58 |
59 |
60 |
But thanks to some great resources I found online I somehow collected the pieces
61 | I needed. And after some more research I also suceeded in putting those pieces together.
62 |
63 |
64 |
65 |
So - to summarize the procedure in my own, CSS-nooby words - what I need to get
66 | what I want:
67 |
68 |
69 |
- a checkbox
70 |
71 | - a CSS :checked selector
72 |
73 | - the subsequent-sibling combinator ~ (wtf, I call it "tilde"...)
74 |
75 | - CSS variables
76 |
77 | - lots of patience because...CSS
78 |
79 |
80 |
81 |
I can really recommend Daniela's fantastic medium article: Dark Mode and CSS variables,
82 | and this guide was also very useful!
83 |
84 |
85 |
HTML
First I needed a checkbox in the html code. I wanted the label to be part of my
86 | navbar so it looks somewhat like this now:
87 |
88 |
89 |
<body>
90 | <input type="checkbox" class="theme-switch" id="theme-switch"/>
91 | <div id="page">
92 | <div>
93 | <nav class="navbar"> ... <a href="#"> <label for="theme-switch" class="switch-label"></label></a></nav>
94 | <div id="content">
95 | ...
96 | </div>
97 | </body>
98 |
99 |
100 |
That's basically what this guide described.
101 |
102 |
103 |
(To get org-mode/ox-slimthml to export the html code like this I modified the export-
104 | function, more about this in the next section)
105 |
106 |
107 |
CSS
And here's what the combination of :checked, CSS variables and the ~ sibling
108 | thing looks like:
109 |
110 |
111 |
:root {
112 |
113 | --light-switch-text: "dark mode";
114 | --light-text: #273136;
115 | --light-bg: #e3e5e6;
116 |
117 | ...
118 |
119 |
120 | --dark-switch-text: "light mode";
121 | --dark-text: #95b1be;
122 | --dark-bg: #14191a;
123 |
124 | ...
125 |
126 |
127 | --switch-text: var(--dark-switch-text);
128 | --text-color: var(--dark-text);
129 | --bg-color: var(--dark-bg);
130 | --theme-color: var(--dark-theme);
131 |
132 | ...
133 |
134 |
135 | .theme-switch:checked ~ #page {
136 | --switch-text: var(--light-switch-text);
137 | --text-color: var(--light-text);
138 | --bg-color: var(--light-bg);
139 | --theme-color: var(--light-theme);
140 |
141 | ...
142 |
143 |
144 |
145 |
So the variables store the colors for the light, dark and default theme (which
146 | is the dark theme in my case).
147 |
148 |
149 |
That's it. Mostly.
150 |
151 |
152 |
And even though I still have no idea about CSS and html, I learned a valuable
153 | lesson: I'm really, really happy that I usually work with C/C++ because
154 | CSS gives me headaches.
155 |
156 |
157 |
Side note: I tried to use css-csslint in Emacs but it doesn't seem to like
158 | CSS variables so I'm using css-stylelint now...
159 |
160 |
161 |
Minimize exported html with ox-slimhtml
This isn't exactly a huge issue that needs fixing but it somehow bothered me (a lot):
162 |
163 |
164 |
in org-mode's exported html you get a lot of these:
165 |
166 |
167 |
168 | <div id="outline-container-org09e108d" class="outline-2">
169 | <h2 id="org09e108d"> some text </h2>
170 | <div class="outline-text-2" id="text-org09e108d">
171 |
172 |
173 |
174 |
That "outline-container-org09e108d" cryptic id-class-whatever stuff kinda hurts
175 | my eyes and I don't need it, so I wanted to get rid of it.
176 |
177 |
178 |
That's how I discovered ox-slimhtml which seems to do exactly what I want (most
179 | of the time).
180 |
181 |
182 |
ox-slimhtml is an org-mode export backend that outputs minimal html and makes
183 | it easier to customize the html output.
184 | Another handy feature of ox-slimhtml is that it allows org-macro expansion in
185 | preamble, postamble, header and footer, quite cool for templating.
186 | Apart from that and slimming the html output it's pretty much the same
187 | as the default org html exporter.
188 |
189 |
190 |
I guess the idea is to create templates for your projects. But because I don't
191 | have (and don't plan to have) other projects than this blog and there are things
192 | I always want in my html, I slightly changed the export function to include
193 | the theme-switch:
194 |
195 |
196 |
(defun my-ox-slimhtml-publish-to-html (plist filename pub-dir)
197 | (let ((file-path (ox-slimhtml-publish-to-html plist filename pub-dir)))
198 | (with-current-buffer (find-file-noselect file-path)
199 | (goto-char (point-min))
200 | (search-forward "<body>")
201 | (insert (mapconcat 'identity
202 | '("<input type=\"checkbox\" class=\"theme-switch\" id=\"theme-switch\"/>"
203 | "<div id=\"page\">")
204 | "\n"))
205 | (goto-char (point-max))
206 | (search-backward "</body>")
207 | (insert "</div></div>")
208 | (save-buffer)
209 | (kill-buffer))
210 | file-path))
211 |
212 |
213 |
For everything else that's specific to certain files I use templates (eg. to add
214 | the stylesheet to files in subfolders).
215 |
216 |
217 |
Apart from generating slimmer html, ox-slimhtml is a lot faster in publishing than the usual org-mode
218 | publishing function.
219 |
220 |
221 |
Something I haven't figured out yet: images don't get exported properly (i.e. not at all ;))
222 | with ox-slimhtml. Currently I'm using a template (YASnippet) for images as well as a workaround,
223 | but it would be nice to find out how to do this without extra steps.
224 |
225 |
226 |
Other changes
Directory structure
I slightly changed the directory structure into something like this:
227 |
228 |
229 |
blog/src/
230 | ├── about.org
231 | ├── archive.org
232 | ├── disclaimer.org
233 | ├── index.org
234 | ├── assets/
235 | ├── css/
236 | │ └── style.css
237 | ├── entwuerfe/
238 | └── posts/
239 | ├── post_1
240 | │ └── index.org
241 | ├── post_2
242 | │ ├── index.org
243 | └── post_3
244 | └── index.org
245 |
246 |
("entwuerfe" = "drafts")
247 |
248 |
Function to get the date from the date keyword
Here's a little context: if you ask Emacs for information about org-publish-find-date
249 | you get the following:
250 |
251 |
252 |
/Find the date of FILE in PROJECT.
253 | This function assumes FILE is either a directory or an Org file.
254 | If FILE is an Org file and provides a DATE keyword use it. In
255 | any other case use the file system’s modification time. Return
256 | time in ‘current-time’ format./
257 |
258 |
259 |
So according to this definition I'd expect it to...well, do what it says it does.
260 | Return the modification time of a file or use the DATE keyword.
261 |
262 |
263 |
Unfortunately in my case it always ignored the DATE keyword and returned not the
264 | modification time but the creation time.
265 |
266 |
267 |
But I really want the date from the DATE keyword. I couldn't figure out
268 | why org-publish-find-date ignores the keyword, so I wrote a function
269 | for that (and again, it would be nice if I didn't need extra steps but hey ¯\_(ツ)_/¯).
270 |
271 |
272 |
(defun my-org-publish-get-date-from-keyword (file project)
273 | "Get date keyword from FILE in PROJECT and parse it to internal format."
274 | (let ((date (org-publish-find-property file :date project)))
275 | (cond ((let ((ts (and (consp date) (assq 'timestamp date))))
276 | (and ts
277 | (let ((value (org-element-interpret-data ts)))
278 | (and (org-string-nw-p value)
279 | (org-time-string-to-time value))))))
280 | (t (error "No timestamp in file \"%s\"" file)))))
281 |
282 |
283 |
And the actual reason why I want the date from the DATE keyword in the first place,
284 | for the sitemap format function:
285 |
286 |
287 |
(defun my-org-blog-sitemap-format-entry (entry _style project)
288 | "Return format string for each ENTRY in PROJECT."
289 | (if (and (s-starts-with? "posts/" entry) (not (equal "disclaimer.org" (file-name-nondirectory entry))))
290 | (format "@@html:<span class=\"archive-item\"><span class=\"archive-date\">@@ %s @@html:</span>@@ [[file:%s][%s]]@@html:<div class=\"description\">@@ %s @@html:</div>@@ @@html:<div class=\"keywords\">@@ %s @@html:</div>@@ @@html:</span>@@"
291 | (format-time-string "[%d.%m.%Y, %R %Z]"
292 | (my-org-publish-get-date-from-keyword entry project))
293 | entry
294 | (org-publish-find-title entry project)
295 | (org-publish-find-property entry :description project 'html)
296 | (org-publish-find-property entry :keywords project 'html))))
297 |
298 |
299 |
300 |
And that's it I guess.
301 |
302 |
303 |
Hopefully this will be the last "major" change, but probably I'll find new excuses to procrastinate from writing
304 | blog posts.
305 |
306 |
Made with Emacs :)
307 |
Disclaimer
308 |