@@" (car li)))
+ (format "* %s" (car li)))
(seq-filter #'car (cdr list))
- "\n")
- "\n#+end_archive\n"))
+ "\n")))
(defun org-blog-publish-to-html (plist filename pub-dir)
"Same as `org-html-publish-to-html' but modifies html before finishing."
@@ -204,16 +215,20 @@ Return output file name."
dst-file))
(copy-file filename dst-file t)))))
+(defalias 'org-blog-posts-sitemap-fn
+ (apply-partially 'org-blog-sitemap-fn-descr posts-descr))
+
+(defalias 'org-blog-micro-sitemap-fn
+ (apply-partially 'org-blog-sitemap-fn-descr micro-descr))
+
(setq org-publish-project-alist
`(("orgfiles"
:base-directory ,base-dir
- :exclude ".*drafts/.*"
+ :exclude ".*(drafts|posts|micro)/.*"
:base-extension "org"
:publishing-directory ,publish-dir
-
:recursive t
:publishing-function org-blog-publish-to-html
-
:with-toc nil
:with-title nil
:with-date t
@@ -226,14 +241,59 @@ Return output file name."
:html-head-extra ,org-blog-head
:html-preamble org-blog-preamble
:html-postamble org-blog-postamble
+ :auto-sitemap nil)
+ ("posts"
+ :base-directory ,posts-dir
+ :base-extension "org"
+ :publishing-directory ,posts-publish-dir
+ :recursive t
+ :publishing-function org-blog-publish-to-html
+ :with-toc nil
+ :with-title nil
+ :with-date t
+ :section-numbers nil
+ :html-doctype "html5"
+ :html-html5-fancy t
+ :html-head-include-default-style nil
+ :html-head-include-scripts nil
+ :htmlized-source t
+ :html-head-extra ,org-blog-head
+ :html-preamble org-blog-preamble
+ :html-postamble org-blog-postamble
:auto-sitemap t
- :sitemap-filename "archive.org"
+ :sitemap-filename "posts.org"
:sitemap-title "Blog Posts"
:sitemap-style list
:sitemap-sort-files anti-chronologically
:sitemap-format-entry org-blog-sitemap-format-entry
- :sitemap-function org-blog-sitemap-function)
+ :sitemap-function org-blog-posts-sitemap-fn)
+
+ ("micro"
+ :base-directory ,micro-dir
+ :base-extension "org"
+ :publishing-directory ,micro-publish-dir
+ :recursive t
+ :publishing-function org-blog-publish-to-html
+ :with-toc nil
+ :with-title nil
+ :with-date t
+ :section-numbers nil
+ :html-doctype "html5"
+ :html-html5-fancy t
+ :html-head-include-default-style nil
+ :html-head-include-scripts nil
+ :htmlized-source t
+ :html-head-extra ,org-blog-head
+ :html-preamble org-blog-preamble
+ :html-postamble org-blog-postamble
+ :auto-sitemap t
+ :sitemap-filename "micro.org"
+ :sitemap-title "Micro Blog Posts"
+ :sitemap-style list
+ :sitemap-sort-files anti-chronologically
+ :sitemap-format-entry org-blog-sitemap-format-entry
+ :sitemap-function org-blog-micro-sitemap-fn)
("assets"
:base-directory ,assets-dir
@@ -252,11 +312,11 @@ Return output file name."
:publishing-directory ,publish-rss-dir
:publishing-function (org-rss-publish-to-rss)
:exclude ".*"
- :include ("archive.org")
+ :include ("posts/posts.org" "micro/micro.org")
:section-numbers nil
:table-of-contents nil)
- ("blog" :components ("orgfiles" "assets" "rss"))))
+ ("blog" :components ("orgfiles" "posts" "assets" "rss"))))
;; add target=_blank and rel="noopener noreferrer" to all links by default
(defun my-org-export-add-target-blank-to-http-links (text backend info)
diff --git a/src/index.org b/src/index.org
index d569fda..74d5029 100644
--- a/src/index.org
+++ b/src/index.org
@@ -8,7 +8,6 @@
#+OPTIONS: H:5
#+STARTUP: showeverything
-
Welcome to my personal website.
- [[file:archive.org][articles]]
diff --git a/src/micro/ping.org b/src/micro/ping.org
new file mode 100644
index 0000000..7ddca83
--- /dev/null
+++ b/src/micro/ping.org
@@ -0,0 +1,11 @@
+#+TITLE: Ping
+#+KEYWORDS: social
+#+AUTHOR: Yann Esposito
+#+EMAIL: yann@esposito.host
+#+DESCRIPTION:
+#+LANGUAGE: en
+#+LANG: en
+#+OPTIONS: H:5 auto-id:t
+#+STARTUP: showeverything
+
+Ping!
diff --git a/src/posts/new-blog.org b/src/posts/new-blog.org
index 8852cf6..ad75d27 100644
--- a/src/posts/new-blog.org
+++ b/src/posts/new-blog.org
@@ -1,5 +1,5 @@
#+TITLE: New Blog
-#+SUBTITLE: Meta Post (not really related to Donal Knuth)
+#+SUBTITLE: Meta Post (not really related to Donald Knuth)
#+AUTHOR: Yann Esposito
#+EMAIL: yann@esposito.host
#+DATE: [2019-08-17 Sat]
diff --git a/src/posts/posts.org b/src/posts/posts.org
new file mode 100644
index 0000000..1e71644
--- /dev/null
+++ b/src/posts/posts.org
@@ -0,0 +1,1172 @@
+#+TITLE: Blog Posts
+#+AUTHOR: Yann Esposito
+#+EMAIL: yann@esposito.host
+#+DESCRIPTION: Articles
+* * [[file:/Users/yaesposi/y/her.esy.fun/src/posts/project-el/index.org][Autoload Script by project]]
+:PROPERTIES:
+:RSS_PERMALINK: project-el/index.html
+:PUBDATE: 2019-08-17
+:ID: FB591D7B-8153-44F5-8D40-3F8C83858529
+:CUSTOM_ID: ----file--users-yaesposi-y-her-esy-fun-src-posts-project-el-index-org--autoload-script-by-project--
+:END:
+#+TITLE: Autoload Script by project
+#+SUBTITLE: fast, secure, easy autoload
+#+AUTHOR: Yann Esposito
+#+EMAIL: yann@esposito.host
+#+DATE: [2019-08-17 Sat]
+#+KEYWORDS: programming, blog, org-mode
+#+OPTIONS: auto-id:t
+
+#+begin_quote
+/tl;dr/: A script that use projectile and GPG to securely load
+an emacs lisp script when opening a new project.
+
+Check the [[#solution]] section to get the code.
+#+end_quote
+
+* Problem
+ :PROPERTIES:
+ :CUSTOM_ID: problem
+ :ID: BD82DF52-CF0F-4E40-8E3A-AA04479E3071
+ :PUBDATE: <2019-08-31 Sat 14:19>
+ :END:
+
+When providing a repository containing only org files of my blog.
+I also wanted to provide everything necessary for users to be able to publish my
+website.
+Emacs, org-publish mostly assume you should put all those details in a
+centralised place in your =~/.emacs.d/.init.el= file.
+
+The main principle is quite simple.
+
+1. When opening a new file in a project, check for the presence of a
+ =project.el= and =project.el.sig= file at the root directory of the
+ project.
+2. Check the file was signed with by a trusted fingerprint.
+3. Load the file.
+
+Other solutions I found on the internet asked you each time you enter in a
+project if you trust the file or not.
+This was both quite annoying and insecure as it is kind of easy to type 'y'
+instead of 'n' and to load 3rd party script.
+
+Note that checking who signed a file with an external signature is not as
+straightforward as it should be:
+
+#+begin_src elisp
+(defun auto-load-project/get-sign-key (file)
+ "Return the fingerprint of they key that signed FILE.
+
+To sign a file you should used
+
+`gpg --local-user my@email --output project.el.sig --detach-sign project.el`"
+ (string-trim-right
+ (shell-command-to-string
+ (concat
+ (format "gpg --status-fd 1 --verify %s.sig %s 2>/dev/null " file file)
+ "|grep VALIDSIG"
+ "|awk '{print $3}'"))))
+#+end_src
+
+- The `--status-fd` should provide more script friendly output.
+ GPG provide localized output by default which are therefore hard to use in
+ script (for grep for example).
+
+We use =projectile= to detect the project-root and when we are in a new project.
+Unfortunately the =projectile-after-switch-project-hooks= doesn't work as I
+expected.
+So I use the hooks =find-file-hook= and =dired-mode-hook= to try to load the
+file.
+In order not to load the code each time, I need to keep a local state of project
+already loaded.
+
+So now, each time I modify the =project.el= I sign it with the following
+command line:
+
+#+begin_src bash
+gpg --local-user my@email --output project.el.sig --detach-sign project.el
+#+end_src
+
+* Solution
+ :PROPERTIES:
+ :CUSTOM_ID: solution
+ :ID: DC21FA06-1531-4944-855A-B61EBA1681B1
+ :PUBDATE: <2019-08-31 Sat 14:19>
+ :END:
+
+The project is hosted here: https://gitlab.esy.fun/yogsototh/auto-load-project-el
+
+You can setup the emacs package in spacemacs with:
+
+#+begin_src elisp
+ ;; ...
+ dotspacemacs-additional-packages
+ '((auto-load-project :location
+ (recipe
+ :fetcher git
+ :url "https://gitlab.esy.fun/yogsototh/auto-load-project-el"
+ :files ("auto-load-project.el"))))
+ ;; ...
+ (defun dotspacemacs/user-config ()
+ ;; ...
+ (require 'auto-load-project)
+ (setq auto-load-project/trusted-gpg-key-fingerprints
+ '("0000000000000000000000000000000000000000" ;; figerprint of trusted key 1
+ "1111111111111111111111111111111111111111"
+ "2222222222222222222222222222222222222222"
+ )))
+ ;; ...
+#+end_src
+
+The full current code should be easy to follow if you have basic notions
+of eLISP:
+
+#+begin_src elisp
+(require 'projectile)
+
+(defvar auto-load-project/trusted-gpg-key-fingerprints
+ '()
+ "The list of GPG fingerprint you trust when decrypting a gpg file.
+You can retrieve the fingerprints of your own private keys
+with: `gpg --list-secret-keys' (take care of removing the
+spaces when copy/pasting here)")
+
+(defun auto-load-project/get-sign-key (file)
+ "Return the fingerprint of they key that signed FILE.
+
+To sign a file you should used
+
+`gpg --local-user my@email --output project.el.sig --detach-sign project.el`"
+ (string-trim-right
+ (shell-command-to-string
+ (concat
+ (format "gpg --status-fd 1 --verify %s.sig %s 2>/dev/null " file file)
+ "|grep VALIDSIG"
+ "|awk '{print $3}'"))))
+
+(defun auto-load-project/trusted-gpg-origin-p (file)
+ "Return non-nil if the FILE is encrypted with a trusted key."
+ (member (auto-load-project/get-sign-key file)
+ auto-load-project/trusted-gpg-key-fingerprints))
+
+(defconst auto-load-project/project-file "project.el"
+ "Project configuration file name.")
+
+(defvar auto-load-project/loaded-projects (list)
+ "Projects that have been loaded by `auto-load-project/load'.")
+
+(defun auto-load-project/load ()
+ "Loads the `auto-load-project/project-file' for a project.
+This is run once the project is loaded signifying project setup."
+ (interactive)
+ (when (projectile-project-p)
+ (lexical-let* ((current-project-root (projectile-project-root))
+ (project-init-file (expand-file-name auto-load-project/project-file current-project-root))
+ (project-sign-file (concat project-init-file ".sig")))
+ (when (and (not (member current-project-root auto-load-project/loaded-projects))
+ (file-exists-p project-init-file)
+ (file-exists-p project-sign-file)
+ (auto-load-project/trusted-gpg-origin-p project-init-file))
+ (message "Loading project init file for %s" (projectile-project-name))
+ (condition-case ex
+ (progn (load project-init-file)
+ (add-to-list 'auto-load-project/loaded-projects current-project-root)
+ (message "%s loaded successfully" project-init-file))
+ ('error
+ (message
+ "There was an error loading %s: %s"
+ project-init-file
+ (error-message-string ex))))))))
+
+(add-hook 'find-file-hook #'auto-load-project/load t)
+(add-hook 'dired-mode-hook #'auto-load-project/load t)
+
+(provide 'auto-load-project)
+#+end_src
+
+* * [[file:/Users/yaesposi/y/her.esy.fun/src/posts/troll-2/index.org][Troll 2]]
+:PROPERTIES:
+:RSS_PERMALINK: troll-2/index.html
+:PUBDATE: 2019-08-17
+:ID: 4CF9E289-E019-4403-8F61-9F7FBC3AB052
+:CUSTOM_ID: ----file--users-yaesposi-y-her-esy-fun-src-posts-troll-2-index-org--troll-2--
+:END:
+#+Title: Troll 2
+#+Subtitle: How a terrible movie can be entertaining
+#+Author: Yann Esposito
+#+Email: yann@esposito.host
+#+Date: [2019-08-17 Sat]
+#+KEYWORDS: movie
+#+DESCRIPTION:
+#+LANGUAGE: en
+#+LANG: en
+#+OPTIONS: H:5 auto-id:t
+#+STARTUP: showeverything
+
+#+begin_notes
+I watched what may be the worse movie of all time and I still enjoyed
+greatly the show.
+#+end_notes
+
+I wanted to watch an horror teen movie I saw when I was a kid; Troll.
+During my searches, I discovered there is another movie named "Troll 2".
+I thought that it is certainly the sequel.
+
+I took a look to the imdb note, and it was very bad (2.9 / 10).
+But, hey, I decided to watch it.
+Here is an overview of my watching experience.
+
+* The watching
+:PROPERTIES:
+:CUSTOM_ID: the-watching
+:ID: F1444E4E-88F3-44EA-8E3B-5A313A558964
+:PUBDATE: <2019-08-31 Sat 14:19>
+:END:
+
+The synopsis of Troll 2 is a family living in a city that exchange its
+house to go to the village of Nilbog "to live like farmers"...
+The son of the family see his dead grandfather.
+The grandpa warn his grandson against Goblins.
+Those creatures make you eat green things that transform you and then they
+eat you.
+Of course, nobody listen to the boy that does not want to go to Nilbog,
+which is infested by goblins that can take Human appearance.
+
+Troll 2 is bad on most criterion you use to measure the quality of a movie.
+
+During the first minutes of the movie, you get to see the costume of the
+goblins.
+Those costume looks very bad and cheap.
+So much you can only find them not terrorizing but funny and ridiculous.
+
+#+CAPTION: One goblin during the introduction scene of Troll 2
+#+NAME: fig:troll-2-intro
+#+ATTR_HTML: A goblin
+[[./Troll-2-intro.jpg]]
+
+Soon after that, you realize the acting of all actors is extremely bad.
+In fact, it is so bad, you might not believe me how bad it is.
+To give you an idea, the only equal bad acting I ever witnessed was while
+looking at amateurs first Youtube movies trying to follow a scenario.
+Apparently most actors were amateurs, it was their first and last movie.
+
+#+CAPTION: One particularly terrible acting scene
+#+NAME: fig:bad-acting
+#+ATTR_HTML: A bad acting demonstration
+[[file:bad-acting.png]]
+
+The dialog are, really something...
+For example the expression "clusters of hemorrhoid" is used in a non ironic
+dialog.
+
+The scenario is terrible.
+For example, most of the thing occurring suffer from terrible plot holes or
+terrible mistakes that make everything hard to believe even if you accept
+the premises of a world were Goblins would exist.
+For example, the grandfather ghost can stop time for 30 seconds for no
+reason at all.
+
+The realization is a series of basic mistakes.
+Actors are not in the same places after all plan cut for example.
+Some filmed scene feel so wrong.
+
+I forgot to give a word about the music.
+It is like the director choose the worst music to go along each scene.
+
+The first ending is really, quite surprising.
+They win against the monsters with, what I believe was a failed attempt at
+humor.
+It misses the point so bad, that the irony still make it funny.
+
+#+CAPTION: Our hero save the day by urinating on the table. His family is frozen for 30s said grandpa, they were for 70s.
+#+NAME: fig:prevent-eating
+#+ATTR_HTML: Eliott prevents his family to eat the food by urinating on the table
+[[./prevent-eating-scene.jpg]]
+
+Of course, the very last scene is a classical so terrible cliché.
+To let you close the experience in awe.
+
+But there is a bonus, the cherry on the cake.
+During all the movie, it is *never* question of a Troll at all.
+*There is not Troll in Troll 2*.
+
+Still, it was quite entertaining.
+
+# LocalWords: Nilbog cliché
+
+* After the movie
+:PROPERTIES:
+:CUSTOM_ID: after-the-movie
+:ID: 44290CAD-7467-4D01-AB15-9A2D5A01B6C5
+:PUBDATE: <2019-08-31 Sat 14:19>
+:END:
+
+What is really interesting though is that I really enjoyed the watch.
+It was so bad, that I couldn't enter in the movie.
+I analyzed the movie, and saw all its failures and noticed everything wrong
+about it.
+In the end, the experience was still quite enjoyable.
+For example, there are some attempts at humor, but almost all of them fail
+terribly.
+I didn't laugh about the joke, but I did nonetheless by thinking about how
+bad that joke was.
+
+Once going to [[https://www.imdb.com/title/tt0105643/][imdb]], I discovered I wasn't alone in loving that terrible
+movie.
+Now, I don't know if I should give that movie a 1 or 2 stars or 8 to 9
+stars because it was so entertaining.
+
+Since then, I learned a few anecdotes about that movie.
+
+It was realized in America by Claudio Fragasso who didn't speak fluent
+English.
+He and his wife were apparently irritated by many of her friends turning
+vegetarian.
+He brought the film crew over with him from Italy.
+None of them spoke English either.
+
+#+begin_quote
+“The cast had few experienced actors, and was primarily assembled from
+residents of nearby towns who responded to an open casting call.
+
+George Hardy was a dentist with no acting experience who showed up for fun,
+hoping to be cast as an extra, only to be given one of the film’s largest
+speaking roles.
+
+Don Packard, who played the store owner, was actually a resident at a
+nearby mental hospital, and was cast for—and filmed—his role while on a day
+trip; after recovering and being released from the hospital, he recalled
+that he had no idea what was happening around him, and that his disturbed
+“performance” in the film was not acting”.
+#+end_quote
+
+Also that movie is so bad, there is a documentary about that movie named:
+[[https://www.imdb.com/title/tt1144539]["Best Worst Movie"]].
+
+
+
+* * [[file:/Users/yaesposi/y/her.esy.fun/src/posts/new-blog.org][New Blog]]
+:PROPERTIES:
+:RSS_PERMALINK: new-blog.html
+:PUBDATE: 2019-08-17
+:ID: 27E9B039-E66E-4419-96BD-6235EA38954A
+:CUSTOM_ID: ----file--users-yaesposi-y-her-esy-fun-src-posts-new-blog-org--new-blog--
+:END:
+#+TITLE: New Blog
+#+SUBTITLE: Meta Post (not really related to Donald Knuth)
+#+AUTHOR: Yann Esposito
+#+EMAIL: yann@esposito.host
+#+DATE: [2019-08-17 Sat]
+#+KEYWORDS: programming, blog, org-mode, web, css
+#+OPTIONS: auto-id:t
+
+#+begin_notes
+tl;dr: The first blog post of a blog should certainly be about the blog
+itself to provide a feeling of self-reference.
+#+end_notes
+
+* Peaceful and Respectful Website
+:PROPERTIES:
+:CUSTOM_ID: peaceful-and-respectful-website
+:ID: 9C715987-0E8C-49CB-8165-94C730989B91
+:PUBDATE: <2019-08-31 Sat 14:19>
+:END:
+
+There is a trend about website being quite less accessible, using more and
+more resources, adding trackers, popups, videos, animations, big js
+frameworks, etc...
+
+I wanted a more peaceful and respectful website.
+
+That website was created with the following constraints in mind by order of
+priority:
+
+1. *Respect Privacy*; no tracker of any sort (no ads, no google analytics, no
+ referrer for all external links, etc...)
+2. *nearly no javascript*; no js at all except for a single exception,
+ pages containing Math formula are displayed using mathjax. That means
+ that event the CSS theme switcher does not use javascript.
+3. *Accessible*; should be easy to read on a text browser so people with
+ disabilities could easily consume it
+4. *nerdy*; should feel mostly like markdown text in a terminal and source
+ code should be syntax highlighted.
+5. *theme switchable*; support your preferred light/dark theme by default
+ but you can change it if you want.
+6. *rss*; you should be able to get informed when I add a new blog post.
+7. *frugal*; try to minimize the resources needed to visit my website; no
+ javascript, no web-font, not too much CSS magic, not much images or really
+ compressed one.
+
+Some of the constraints are straightforward to get, some not.
+
+You can also check that not using more resources one of the theme of my
+website looks quite more classical and modern that my preferred ones.
+
+** Respect Privacy
+:PROPERTIES:
+ :CUSTOM_ID: respect-privacy
+ :ID: 085ADDF5-D910-4D0E-BA26-F07060D89E37
+ :END:
+The one should be easy, simply not put any 3rd party inclusion in my website.
+So, no external CSS in my headers, no link to any image I do not host myself.
+No 3rd party javascript.
+
+** Javascript Free
+:PROPERTIES:
+ :CUSTOM_ID: javascript-free
+ :ID: 882ED09A-B7E9-4AD9-AB15-9C817EE000B3
+ :END:
+I do not really see why a content oriented website should need to execute javascript.
+
+** Accessible
+:PROPERTIES:
+:CUSTOM_ID: disability-friendly
+:ID: 854BF110-84CF-45D3-99A4-4F7473BFD6DC
+:END:
+A good way to check that a website is friendly to disabled people is by
+looking at it with a text browser.
+If you open most website today you see that at the top of the page is
+crippled with a numerous number of links/metas info used for javascript
+tricks, login/logout buttons, etc...
+The website should only contain, a pretty minimal menu to navigate, and the
+content.
+
+** Nerdy
+:PROPERTIES:
+:CUSTOM_ID: nerdy
+:ID: 08E2BE72-C7FD-4E86-92BF-1DB97B9A6F44
+:END:
+The feel of the website should be nerdy, it should look like reading a
+terminal or emacs.
+It should almost feel the same as if you were using a text-browser.
+For sensible people, I added a "modern" theme that should better suit
+modern eye, still the first design should always be the terminal looking
+one.
+
+** Theme switchable
+:PROPERTIES:
+:CUSTOM_ID: theme-switchable
+:ID: B2061EBE-C79C-49B8-AC57-5FA5D6961F21
+:END:
+Even if you are not used to disability friendly browser.
+The website should try to guess your preferred way to consume my website.
+Recently we dark/light themes were integrated as a new CSS feature.
+This website should propose your apparently preferred theme.
+But you could also change it manually.
+
+** RSS
+:PROPERTIES:
+:CUSTOM_ID: rss
+:ID: F21DD644-92A1-4BEA-A3AC-4AB8BA03A60A
+:END:
+This is another layer that help you consume my website as you prefer.
+You should at least be informed a new article has been published.
+
+** Frugal
+:PROPERTIES:
+:CUSTOM_ID: frugal
+:ID: 29FFEBDC-B978-44E3-9C33-CB6302632037
+:END:
+This one is a bit tricky.
+It would mean, that visiting my website should not consume much resources.
+Mainly, this would prevent using heavy medias as much as possible.
+So, no video, no animated gif, no image if possible or very compressed small one.
+So I have a script that convert all images to maximize site to `800x800`
+and use at max 16 colors. On my current example image the size goes from 3.1MB to 88KB.
+
+* How
+:PROPERTIES:
+:CUSTOM_ID: how
+:ID: 21EA558C-C9BB-45F2-8F17-B544DBA6AC44
+:PUBDATE: <2019-08-31 Sat 14:19>
+:END:
+** CSS
+:PROPERTIES:
+:CUSTOM_ID: css
+:ID: B117AFAE-BBA4-4DFB-B171-5B60B4E67412
+:END:
+Regarding CSS, I always found that the default text display by navigator is
+terrible.
+So just to "fix" a minimal CSS to have something bearable it takes me about
+120 lines of CSS.
+
+By fixing I mean things like using a fixed line width for text (there is an
+optimal range to improve legibility).
+Also having correct list indentation, line-height and font-size.
+Table displaying correctly.
+
+Then I have about 90 lines of CSS to make my HTML look like text source of
+a markdown.
+
+Then I set a few CSS rules to handle the ids and classes added by
+org-export as instead of using the ubiquitous Markdown, I prefer greatly to
+use org mode files.
+I need 60 lines of CSS for them.
+
+In order to handle color themes (5 at the time of writing those lines) I
+use almost 350 line to handle those.
+
+*** CSS Theme selection
+:PROPERTIES:
+:CUSTOM_ID: css-theme-selection
+:ID: 4061F98F-1AA4-441A-A073-6E666A4B1AA1
+:END:
+
+One thing that wasn't straightforward while writing the CSS was to provide
+an interactive theme selector without any javascript involved.
+That theme switcher is really the limit I can concede to modern standards
+because it is CSS only.
+
+The trick is to provide one top-level element per theme at the beginning of
+the body of the HTML.
+Then hide those elements (I chose inputs).
+Finally provide a set of anchor links.
+
+#+begin_src html
+ ...
+
+
+
+
+
+#+end_src
+
+Then use the /sibling/ CSS selector =~=.
+Then put all your content in a div of class =.main= for example.
+Finally in the CSS you can write things like:
+
+#+begin_src css
+ /* hide all radio button that are not inside another div of body */
+ body > input {
+ display: none;
+ }
+ :root {
+ --light-color: #fff;
+ --dark-color: #000;
+ }
+ input#light:target ~ .main {
+ background-color: var(--light-color);
+ color: var(--dark-color);
+ }
+ input#dark:target ~ .main {
+ background-color: var(--dark-color);
+ color: var(--light-color);
+ }
+#+end_src
+
+I previously used checkbox inputs but using URL fragment feels better.
+
+** Blog Engine - org-mode with org-publish
+:PROPERTIES:
+:CUSTOM_ID: blog-engine---org-mode-with-org-publish
+:ID: 674088E6-C71B-4C48-B785-43BE8C4A2ADB
+:END:
+So publishing a website is something that could go from.
+Write your own HTML each time.
+But this is quite tedious, so we generally all use a website generator.
+The next thing with the minimal possible amount of work is using org-mode
+with org-publish.
+Because a website is mostly, export all of file in org-mode format (easier
+to write and manipulate than raw HTML) to HTML.
+
+In fact, there are numerous details that make this task not this straightforward.
+You want:
+
+1. from a tree of org-mode files, generate an equivalent file tree of HTML
+ files generated from the org-mode files. This is the main purpose of org-publish.
+2. We also want to set specific headers, a CSS file, a favicon, link to RSS
+ file, mobile friendly directives.
+3. Have common header/footer (preamble, postamble) if possible with a menu.
+4. An archive page with a list of posts.
+5. Generate an RSS file
+6. Niceties:
+ - obfuscate your email to prevent spam
+ - link to your email with a link to the current page integrated in the body/subject
+ - replace your external link to open in a new tab securely (=noopener / noreferrer=).
+ - compress images during publishing
+
+Also, a single detail make using org-publish a bit awkward compared to
+classical other classical static website generators; it is designed to be
+set in you full emacs configuration.
+But I wanted to be able to clone my git repository and be able to generate
+my website locally even if I clone it on different directories.
+
+So I created a package just for that: [[file:project-el/index.org][Autoload eLISP file in projects]].
+
+*** Tree of files
+:PROPERTIES:
+:CUSTOM_ID: tree-of-files
+:ID: 9025C6FA-C1DE-4194-AE19-A1E735FDF6FD
+:END:
+
+There is a first pass that use =projectile= emacs package to detect the
+current root file of the project and provide a list of absolute paths.
+
+Then you set the associative list =org-publish-project-alist= with many
+straightforward details.
+The source directory, the destination directory, but also, file to exclude,
+a function used to transform org files to HTML, etc...
+
+#+begin_src elisp
+ (setq domainname "https://john.doe")
+ (setq base-dir (concat (projectile-project-root) "src"))
+ (setq publish-dir (concat (projectile-project-root) "_site"))
+ (setq assets-dir (concat base-dir "/"))
+ (setq publish-assets-dir (concat publish-dir "/"))
+ (setq rss-dir base-dir)
+ (setq rss-title "Subscribe to articles")
+ (setq publish-rss-dir publish-dir)
+ (setq publish-rss-img (concat domainname "/rss.png"))
+ (setq css-path "/css/minimalist.css")
+ (setq author-name "John Doe")
+ (setq author-email "john@doe.com")
+
+ (require 'org)
+ (require 'ox-publish)
+ (require 'ox-html)
+ (require 'org-element)
+ (require 'ox-rss)
+
+ (setq org-link-file-path-type 'relative)
+ (setq org-publish-timestamp-directory
+ (concat (projectile-project-root) "_cache/"))
+
+ (setq org-publish-project-alist
+ `(("orgfiles"
+ :base-directory ,base-dir
+ :exclude ".*drafts/.*"
+ :base-extension "org"
+ :publishing-directory ,publish-dir
+ :recursive t
+ :publishing-function org-blog-publish-to-html
+
+ :with-toc nil
+ :with-title nil
+ :with-date t
+ :section-numbers nil
+ :html-doctype "html5"
+ :html-html5-fancy t
+ :html-head-include-default-style nil
+ :html-head-include-scripts nil
+ :htmlized-source t
+ :html-head-extra ,org-blog-head
+ :html-preamble org-blog-preamble
+ :html-postamble org-blog-postamble
+
+ :auto-sitemap t
+ :sitemap-filename "archive.org"
+ :sitemap-title "Blog Posts"
+ :sitemap-style list
+ :sitemap-sort-files anti-chronologically
+ :sitemap-format-entry org-blog-sitemap-format-entry
+ :sitemap-function org-blog-sitemap-function)
+
+ ("assets"
+ :base-directory ,assets-dir
+ :base-extension ".*"
+ :exclude ".*\.org$"
+ :publishing-directory ,publish-assets-dir
+ :publishing-function org-blog-publish-attachment
+ :recursive t)
+
+ ("rss"
+ :base-directory ,rss-dir
+ :base-extension "org"
+ :html-link-home ,domainname
+ :html-link-use-abs-url t
+ :rss-extension "xml"
+ :rss-image-url ,publish-rss-img
+ :publishing-directory ,publish-rss-dir
+ :publishing-function (org-rss-publish-to-rss)
+ :exclude ".*"
+ :include ("archive.org")
+ :section-numbers nil
+ :table-of-contents nil)
+
+ ("blog" :components ("orgfiles" "assets" "rss"))))
+#+end_src
+*** HTML Headers
+:PROPERTIES:
+:CUSTOM_ID: html-headers
+:ID: 97990AD3-218F-46E5-A5CD-7DCDDDF34653
+:END:
+
+I set the header to provide a link to the RSS file, the CSS, the favicon
+and viewport directive for mobile browsers.
+
+#+begin_src elisp
+ (defvar org-blog-head
+ (concat
+ ""
+ ""
+ ""
+ ""))
+
+#+end_src
+*** Preamble & Postamble
+:PROPERTIES:
+:CUSTOM_ID: preamble---postamble
+:ID: 43B0C7DD-4CC8-4A6D-8E72-D8C3A242D927
+:END:
+
+So I put a menu in both the preamble and postamble.
+The postamble contains a lot of details about the article, author, email,
+date, etc...
+
+#+begin_src elisp
+(defun menu (lst)
+ "Blog menu"
+ (concat
+ ""
+ (mapconcat 'identity
+ (append
+ '("Home"
+ "Posts"
+ "About")
+ lst)
+ " | ")
+ ""))
+
+ (defun get-from-info (info k)
+ (let ((i (car (plist-get info k))))
+ (when (and i (stringp i))
+ i)))
+
+ (defun org-blog-preamble (info)
+ "Pre-amble for whole blog."
+ (concat
+ "