Updates, fragment system to change theme.

This commit is contained in:
Yann Esposito (Yogsototh) 2019-08-16 22:51:11 +02:00
parent 42a8465ad1
commit 0a5d0ed2ff
Signed by untrusted user who does not match committer: yogsototh
GPG Key ID: 7B19A4C650D59646
4 changed files with 779 additions and 183 deletions

View File

@ -48,11 +48,11 @@ li {
}
ul > li:before {
content: " ";
font-weight: bold;
opacity: 0.5;
float: left;
position: relative;
left: -20px;
text-align: right;
width: 0;
}
ol {
@ -61,12 +61,18 @@ ol {
ol > li:before {
content: counter(ol) ". ";
counter-increment: ol;
font-weight: bold;
float: left;
text-align: right;
position: relative;
left: -20px;
width: 0;
}
ol > li:nth-child(n+10) {
padding-left:28px;
}
ol > li:nth-child(n+10):before {
left: -28px;
}
img {
max-width: 100%;
max-height: 800px;
@ -314,6 +320,7 @@ navigation > a {
body > input { display:none; }
/* Light theme checked */
input#light:target ~ .main,
input#light:checked ~ .main {
--main-background: #fefaf0; /* 0.5 lighter than #fdf6e3 */
--main-foreground: var(--base01);
@ -323,12 +330,14 @@ input#light:checked ~ .main {
--border-color: var(--base2);
--todo-txt: #FFF;
}
input#light:target ~ #labels,
input#light:checked ~ #labels {
background: #fefaf0;
color: var(--base00);
}
/* Dark theme checked */
input#dark:target ~ .main,
input#dark:checked ~ .main {
--main-background: var(--base03); /* 0.5 darker than #002b36; */
--main-foreground: var(--base1);
@ -338,12 +347,14 @@ input#dark:checked ~ .main {
--border-color: var(--base02);
--todo-txt: #000;
}
input#dark:target ~ #labels,
input#dark:checked ~ #labels {
background: var(--base03);
color: var(--base0);
}
/* Light raw theme checked */
input#raw:target ~ .main,
input#raw:checked ~ .main {
--main-background: #fff;
--main-foreground: #000;
@ -361,16 +372,20 @@ input#raw:checked ~ .main {
--color-link: var(--magenta);
font-family: monospace;
}
input#raw:target ~ #labels,
input#raw:checked ~ #labels {
background: #fff;
color: #000;
}
input#raw:target ~ .main code,
input#raw:target ~ .main pre,
input#raw:checked ~ .main code,
input#raw:checked ~ .main pre
{
font-family: monospace;
}
/* Light modern theme checked */
input#modern:target ~ .main,
input#modern:checked ~ .main {
--main-background: #fff;
--main-foreground: #444;
@ -385,10 +400,27 @@ input#modern:checked ~ .main {
font-size: 16px;
line-height: 24px;
}
input#modern:target ~ .main .content,
input#modern:target ~ .main #content,
input#modern:checked ~ .main .content,
input#modern:checked ~ .main #content {
margin: 0 auto;
}
input#modern:target ~ .main blockquote:after,
input#modern:checked ~ .main blockquote:after {
display: none;
}
input#modern:target ~ .main blockquote,
input#modern:checked ~ .main blockquote {
font-family: "Hoefler Text", Georgia, serif;
font-style: italic;
font-size: 20px;
line-height: 30px;
border-left: solid 10px;
}
input#modern:target ~ .main h1,
input#modern:target ~ .main h2,
input#modern:checked ~ .main h1,
input#modern:checked ~ .main h2 {
font-family: "Hoefler Text", Georgia, serif;
@ -397,6 +429,10 @@ input#modern:checked ~ .main h2 {
margin: 1em 0;
}
input#modern:target ~ .main h3,
input#modern:target ~ .main h4,
input#modern:target ~ .main h5,
input#modern:target ~ .main h6,
input#modern:checked ~ .main h3,
input#modern:checked ~ .main h4,
input#modern:checked ~ .main h5,
@ -405,9 +441,16 @@ input#modern:checked ~ .main h6 {
font-size: 1.5em;
margin: 1em 0;
}
input#modern:target ~ .main #preamble h1,
input#modern:checked ~ .main #preamble h1 {
font-size: 4em;
}
input#modern:target ~ .main h1::before,
input#modern:target ~ .main h2::before,
input#modern:target ~ .main h3::before,
input#modern:target ~ .main h4::before,
input#modern:target ~ .main h5::before,
input#modern:target ~ .main h6::before,
input#modern:checked ~ .main h1::before,
input#modern:checked ~ .main h2::before,
input#modern:checked ~ .main h3::before,
@ -416,37 +459,48 @@ input#modern:checked ~ .main h5::before,
input#modern:checked ~ .main h6::before {
content: "";
}
input#modern:target ~ .main pre,
input#modern:checked ~ .main pre {
font-size: 14px;
line-height; 21px;
border-left: solid 4px var(--soft-foreground);
padding-left: 1em;
}
input#modern:target ~ .main pre::before,
input#modern:target ~ .main pre::after,
input#modern:checked ~ .main pre::before,
input#modern:checked ~ .main pre::after {
content: "";
}
input#modern:target ~ .main #preamble,
input#modern:target ~ .main #postamble,
input#modern:checked ~ .main #preamble,
input#modern:checked ~ .main #postamble {
background-color: var(--cyan);
padding: 2em 0;
}
input#modern:target ~ .main #postamble code,
input#modern:checked ~ .main #postamble code {
background-color: transparent;
}
input#modern:target ~ .main #preamble *,
input#modern:target ~ .main #postamble *,
input#modern:checked ~ .main #preamble *,
input#modern:checked ~ .main #postamble * {
color: #fff;
}
input#modern:target ~ #labels,
input#modern:checked ~ #labels {
background: var(--cyan);
color: #fff;
}
input#modern:target ~ #labels .content,
input#modern:checked ~ #labels .content {
margin: 0 auto;
}
/* Dark raw theme checked */
input#darkraw:target ~ .main,
input#darkraw:checked ~ .main {
--main-background: #000;
--main-foreground: #fff;
@ -464,10 +518,13 @@ input#darkraw:checked ~ .main {
--color-link: var(--yellow);
font-family: monospace;
}
input#darkraw:target ~ #labels,
input#darkraw:checked ~ #labels {
background: #000;
color: #fff;
}
input#darkraw:target ~ .main code,
input#darkraw:target ~ .main pre,
input#darkraw:checked ~ .main code,
input#darkraw:checked ~ .main pre
{

View File

@ -4,7 +4,7 @@
#+KEYWORDS: programming
#+DESCRIPTION: css demonstration
#+OPTIONS: H:5
#+OPTIONS: auto-id:t
#+OPTIONS: auto-id:t toc:t
* Level 1 in org-mode
:PROPERTIES:
@ -18,7 +18,7 @@ Here is a [[file:index.html][link to another page]].
There should be whitespace between paragraphs.
Mulitples lines
Multiples lines
in the source
code should
be set as a single
@ -26,96 +26,156 @@ paragraph.
But if I use a double space
There should be a forced newline.
* Lorem Ipsum
:PROPERTIES:
:CUSTOM_ID: lorem-ipsum
:END:
#+begin_src http :pretty
GET https://jaspervdj.be/lorem-markdownum/markdown.txt
#+end_src
** Deos illo sacrum perque victorem eurytus paterque
:PROPERTIES:
:CUSTOM_ID: deos-illo-sacrum-perque-victorem-eurytus-paterque
:END:
*** Haec tela discedentem femina
:PROPERTIES:
:CUSTOM_ID: haec-tela-discedentem-femina
:END:
Lorem markdownum invidiosa iactor! Fidem utque indeploratum pariter.
#+BEGIN_SRC javascript
var port = tooltipPimThreading;
if (pinterest_net_koffice) {
format -= installer;
buffer = 4;
office_device(2, ssh + model);
} else {
website_controller_linux.gigabyte = clone;
}
fileProtocol = driverOcr;
#+END_SRC
*** Duorum habuisse horrentia de noceat
:PROPERTIES:
:CUSTOM_ID: duorum-habuisse-horrentia-de-noceat
:END:
Patriam vocalibus peraget robore recursus diruta ut ergo signa factis,
nigrum, attonitas, in Solis. Mugit [[http://news.ycombinator.com][non
carpe sed]] ceperunt dextra ausum; sanguine durior. Soror Cinyras illos
remugis; super quia somno nunc retemptat oracula cumque. Id Helicen
primum, proles turis nemus claudit molior naturalique deciperetur qua
sulcat et, ad volui.
1. Tubas gener
2. Ad pacta
3. Vetantis ferrum pugnabant quoque
4. Diomede pallida boum membra
Saepe /mihi/? Tum comae iurasse statione morsus vigilans, ergo. Ipse dum
sunt Hymettia: fecit cum et coactis, potae carmine lynca fertur
descenderat.
*** Subitis de luce et poenas cum
:PROPERTIES:
:CUSTOM_ID: subitis-de-luce-et-poenas-cum
:END:
Cladem adit ergo es tecto sit visa dryades vota quos, prima ligno ficta.
Ibat supplex, /est/ costis oblita, elisi regis captum Phoebi tandem
conchaeque! Aera et Aello genetrix.
Et caelo adspexit, cornua et Iuppiter inmensae /abies/. Micabant Paeonas
meliora constitit, ut expers *artisque* adspicit fretum, vulnere
oppidaque, tenuit arma, haec velamine unda. Retinentia iudice, *quos
moras* comitata aliquid prospiciens cepit cum iustius sua cur truncis
ausus unde partibus marisque et grave. Fui opertis liquor laudatae di
erat, repellit conspectos manibus.
Auresque longius res Fando es /nil nec ora/ vixque et tot pendens maius
in et meliora! Flentibus verba nostro agmenque: dedit est regis virque
post Talia telumque, sol *et lanient* perierunt timido. Fitque variare
truncas Phoebus agros, salictis tam pumice facti! Post et neque et
vidit, Lycaeo incautus, latratus terrae thalamique ulta.
* Math
:PROPERTIES:
:CUSTOM_ID: math
:END:
\(x^y / \log(x)\)
Let also try to write some math inside the text $\pi_1(X,x_0)$
\(x^y / \log(x)\)
\[ \prod_{i=0}^n \sum_{x_i\in E} \frac{1}{x_i} \]
* Blocks
:PROPERTIES:
:CUSTOM_ID: blocks
:END:
** Notes
:PROPERTIES:
:CUSTOM_ID: notes
:END:
#+begin_notes
This is some note.
With a bit of information and text.
Perhaps a list:
- thing
- another thing
#+end_notes
** Examples
:PROPERTIES:
:CUSTOM_ID: examples
:END:
#+begin_example
This is an example.
We can say a few things.
Perhaps a list:
- thing
- another thing
#+end_example
* Lists
:PROPERTIES:
:CUSTOM_ID: lists
:END:
** Ordered lists
:PROPERTIES:
:CUSTOM_ID: ordered-lists
:END:
1. counting with very very long lines. Just to check that everything is all
right regarding very long lines inside list, but also nested lists.
2. foo
3. bar
4. baz
5. counting
6. up
7. to
8. a
9. big
10. number enough
11. to have two
12. digits
13. The simplest example of a homotopy group is the /fundamental group/ of
a space, which is written $\pi_1(X,x_0)$: Given a space $X$ and a point
$x_0$ in it, one can make a group whose elements are loops at $x_0$
(continuous paths from $x_0$ to $x_0$), considered up to homotopy, with
the group operations given by the identity path (standing still), path
concatenation, and path reversal. For example, the fundamental group of
the $2$-sphere is trivial, but the fundamental group of the torus is
not, which shows that the sphere and the torus are not homotopy
equivalent. The intuition is that every loop on the sphere is homotopic
to the identity, because its inside can be filled in. In contrast, a
loop on the torus that goes through the donut's hole is not homotopic
to the identity, so there are non-trivial elements in the fundamental
group.
14. Lot of things.
** Unordered list
:PROPERTIES:
:CUSTOM_ID: unordered-list
:END:
- counting with very very long lines. Just to check that everything is all
right regarding very long lines inside list, but also nested lists.
- foo
- bar
- baz
- counting
- up
- to
- a
- big
- number enough
- to have two
- digits
- The simplest example of a homotopy group is the /fundamental group/ of
a space, which is written $\pi_1(X,x_0)$: Given a space $X$ and a point
$x_0$ in it, one can make a group whose elements are loops at $x_0$
(continuous paths from $x_0$ to $x_0$), considered up to homotopy, with
the group operations given by the identity path (standing still), path
concatenation, and path reversal. For example, the fundamental group of
the $2$-sphere is trivial, but the fundamental group of the torus is
not, which shows that the sphere and the torus are not homotopy
equivalent. The intuition is that every loop on the sphere is homotopic
to the identity, because its inside can be filled in. In contrast, a
loop on the torus that goes through the donut's hole is not homotopic
to the identity, so there are non-trivial elements in the fundamental
group.
- Lot of things.
** Nested
:PROPERTIES:
:CUSTOM_ID: nested
:END:
- counting with very very long lines. Just to check that everything is all
right regarding very long lines inside list, but also nested lists.
- foo
- bar
- baz
- quux
- The simplest example of a homotopy group is the /fundamental group/ of
a space, which is written $\pi_1(X,x_0)$: Given a space $X$ and a point
$x_0$ in it, one can make a group whose elements are loops at $x_0$
(continuous paths from $x_0$ to $x_0$), considered up to homotopy, with
the group operations given by the identity path (standing still), path
concatenation, and path reversal.
1. counting with very very long lines. Just to check that everything is all
right regarding very long lines inside list, but also nested lists.
2. foo
1. Something else to nest.
2. For example, the fundamental group of the $2$-sphere is trivial,
but the fundamental group of the torus is not, which shows that the
sphere and the torus are not homotopy equivalent. The intuition is
that every loop on the sphere is homotopic to the identity, because
its inside can be filled in. In contrast, a loop on the torus that
goes through the donut's hole is not homotopic to the identity, so
there are non-trivial elements in the fundamental group.
3. The simplest example of a homotopy group is the /fundamental group/ of
a space, which is written $\pi_1(X,x_0)$: Given a space $X$ and a point
$x_0$ in it, one can make a group whose elements are loops at $x_0$
(continuous paths from $x_0$ to $x_0$), considered up to homotopy, with
the group operations given by the identity path (standing still), path
concatenation, and path reversal.
4. Lot of things.
- Lot of things.
* Level 1
:PROPERTIES:
:CUSTOM_ID: level-1
:END:
** Level 2
** Level 2 - paragraph
:PROPERTIES:
:CUSTOM_ID: level-2-85fc
:END:
@ -124,7 +184,7 @@ vidit, Lycaeo incautus, latratus terrae thalamique ulta.
GitHub is a code hosting platform for version control and collaboration.
It lets you and others work together on projects from anywhere.
*** Level 3
*** Level 3 - blockquote
:PROPERTIES:
:CUSTOM_ID: level-3
:END:
@ -136,7 +196,7 @@ vidit, Lycaeo incautus, latratus terrae thalamique ulta.
favor.
#+end_quote
**** Level 4
**** Level 4 - source code
:PROPERTIES:
:CUSTOM_ID: level-4
:END:
@ -172,39 +232,10 @@ vidit, Lycaeo incautus, latratus terrae thalamique ulta.
putStrLn $ "Hello " <> name <> "!"
#+end_src
***** Level 5
:PROPERTIES:
:CUSTOM_ID: level-5
:END:
- this is an unordered list following a header.
- this is an unordered list following a header. This time with quite a long
text that should goes to the line. Let's write a bit more text with some
=code= here, just to be sure that it use many lines.
- this is an unordered list following a header.
1. this is an ordered list.
2. this is an ordered list that will use many lines. I will fill it with a bit
of random text. Also I will but some =code= inside it, with /italic/ and *bold*
text just to check the line-height.
- sub unordered
- sub sub unordered
- Ok, Haxx0r ipsum concurrently malloc leet new injection bin epoch daemon
system Leslie Lamport shell spoof. Finally boolean continue mutex endif
its a feature gobble packet public irc race condition grep ifdef Im
compiling. Try catch bar bytes Im sorry Dave, Im afraid I cant do
that pragma win sql gnu kilo deadlock blob try catch exception Donald
Knuth brute force.
- unordered
1. ordered again
2. yep :)
3. this is an ordered list following a header.
1. sub ordered
2. second
1. sub sub ordered
2. still
3. here
4. this is an ordered list following a header.
***** Level 5 - table and rules
:PROPERTIES:
:CUSTOM_ID: level-5
:END:
| head1 | head two |
|-----------+---------------------------|
@ -227,32 +258,10 @@ Bad too wide table...
------
*** Here is an unordered list:
:PROPERTIES:
:CUSTOM_ID: here-is-an-unordered-list-
:END:
- level 1 item
- level 2 item
- level 2 item
- level 2 item
- level 3 item
- level 3 item
- level 1 item
- level 2 item
- level 3 item
- level 3 item
- level 2 item
- level 2 item
- level 1 item
- level 2 item
- level 2 item
- level 2 item
**** Image test
:PROPERTIES:
:CUSTOM_ID: image-test
:END:
* Image
:PROPERTIES:
:CUSTOM_ID: image
:END:
an image:
@ -268,41 +277,43 @@ just to check.
- [[../img/a.png]] item with img
- [[../img/a.png]] item with img
- [[../img/a.png]] item with img
- [[../img/a.png]] item with img
***** TODO todo
:PROPERTIES:
:CUSTOM_ID: todo
:END:
****** IN-PROGRESS in-progress
:PROPERTIES:
:CUSTOM_ID: in-progress
:END:
******* IN-REVIEW in-review
:PROPERTIES:
:CUSTOM_ID: in-review
:END:
****** HOLD on hold state
:PROPERTIES:
:CUSTOM_ID: on-hold-state
:END:
- State "HOLD" from "IN-REVIEW" [2019-07-09 Tue 13:44] \\
some reason
****** WAITING waiting status
:PROPERTIES:
:CUSTOM_ID: waiting-status
:END:
- State "WAITING" from [2019-07-09 Tue 13:44] \\
waitin for someone
****** DONE done status
:PROPERTIES:
:CUSTOM_ID: done-status
:END:
****** CANCELED canceled status
CLOSED: [2019-07-09 Tue 13:45]
:PROPERTIES:
:CUSTOM_ID: canceled-status
:END:
- State "CANCELED" from [2019-07-09 Tue 13:45] \\
cancel reason
* TODOs
:PROPERTIES:
:CUSTOM_ID: todos
:END:
****** TODO todo
:PROPERTIES:
:CUSTOM_ID: todo
:END:
******* IN-PROGRESS in-progress
:PROPERTIES:
:CUSTOM_ID: in-progress
:END:
******** IN-REVIEW in-review
:PROPERTIES:
:CUSTOM_ID: in-review
:END:
******* HOLD on hold state
:PROPERTIES:
:CUSTOM_ID: on-hold-state
:END:
- State "HOLD" from "IN-REVIEW" [2019-07-09 Tue 13:44] \\
some reason
******* WAITING waiting status
:PROPERTIES:
:CUSTOM_ID: waiting-status
:END:
- State "WAITING" from [2019-07-09 Tue 13:44] \\
waiting for someone
******* DONE done status
:PROPERTIES:
:CUSTOM_ID: done-status
:END:
******* CANCELED canceled status
CLOSED: [2019-07-09 Tue 13:45]
:PROPERTIES:
:CUSTOM_ID: canceled-status
:END:
- State "CANCELED" from [2019-07-09 Tue 13:45] \\
cancel reason

View File

@ -18,7 +18,9 @@ priority:
1. *Respect Privacy*; no tracker of any sort (no ads, no google analytics, no
referrer for all external links, etc...)
2. *javascript free*; no js at all, yes even the theme changer
2. *almost javascript free*; 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. *disability friendly*; 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

View File

@ -27,7 +27,9 @@ priority:
1. *Respect Privacy*; no tracker of any sort (no ads, no google analytics, no
referrer for all external links, etc...)
2. *javascript free*; no js at all, yes even the theme changer
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
@ -188,6 +190,24 @@ Finally in the CSS you can write things like:
}
#+end_src
I also added a system to support theme using fragment in the URL.
#+begin_src css
input#light:target ~ .main,
input#light:checked ~ .main {
background-color: var(--light-color);
color: var(--dark-color);
}
input#dark:target ~ .main,
input#dark:checked ~ .main {
background-color: var(--dark-color);
color: var(--light-color);
}
#+end_src
Unfortunately, the fragment system take full priority other the checkbox
mechanism.
Regarding selecting the user preferred theme, there are plenty of tutorial
on the internet, you could also simply steal my CSS.
@ -204,3 +224,509 @@ 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=).
*** Tree of files
:PROPERTIES:
:CUSTOM_ID: tree-of-files
:END:
#+begin_src elisp
(setq domainname "https://her.esy.fun")
(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 "Yann Esposito")
(setq author-email "yann@esposito.host")
(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-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
:END:
#+begin_src elisp
(defvar org-blog-head
(concat
"<link rel=\"stylesheet\" type=\"text/css\" href=\"" css-path "\"/>"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
"<link rel=\"alternative\" type=\"application/rss+xml\" title=\"" rss-title "\" href=\"/archives.xml\" />"
"<link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/favicon.ico\">"))
#+end_src
*** Preamble & Postamble
:PROPERTIES:
:CUSTOM_ID: preamble---postamble
:END:
#+begin_src elisp
(defun menu (lst)
"Blog menu"
(concat
"<navigation>"
(mapconcat 'identity
(append
'("<a href=\"/index.html\">Home</a>"
"<a href=\"/archive.html\">Posts</a>"
"<a href=\"/about-me.html\">About</a>")
lst)
" | ")
"</navigation>"))
(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
"<div class=\"content\">"
(menu '("<a href=\"#postamble\">↓ bottom ↓</a>"))
"<h1>"
(format "%s" (car (plist-get info :title)))
"</h1>"
(when-let ((date (plist-get info :date)))
(format "<span class=\"article-date\">%s</span>"
(format-time-string "%Y-%m-%d"
(org-timestamp-to-time
(car date)))))
(when-let ((subtitle (car (plist-get info :subtitle))))
(format "<h2>%s</h2>" subtitle))
"</div>"))
(defun org-blog-postamble (info)
"Post-amble for whole blog."
(concat
"<div class=\"content\">"
;; TODO install a comment system
;; (let ((url (format "%s%s" domainname (replace-regexp-in-string base-dir "" (plist-get info :input-file)))))
;; (format "<a href=\"https://comments.esy.fun/slug/%s\">comment</a>"
;; (url-hexify-string url)))
"<footer>"
(when-let ((author (get-from-info info :author)))
(if-let ((email (plist-get info :email)))
(let* ((obfs-email (obfuscate-html email))
(obfs-author (obfuscate-html author))
(obfs-title (obfuscate-html (get-from-info info :title)))
(full-email (format "%s &lt;%s&gt;" obfs-author obfs-email)))
(format "<div class=\"author\">Author: <a href=\"%s%s%s%s\">%s</a></div>"
(obfuscate-html "mailto:")
full-email
(obfuscate-html "?subject=yblog: ")
obfs-title
full-email))
(format "<div class=\"author\">Author: %s</div>" author)))
(when-let ((date (plist-get info :date)))
(format "<div class=\"date\">Created: %s</div>"
(format-time-string "%Y-%m-%d"
(org-timestamp-to-time
(car date)))))
(when-let ((keywords (plist-get info :keywords)))
(format "<div class=\"keywords\">Keywords: <code>%s</code></div>" keywords))
(format "<div class=\"date\">Generated: %s</div>"
(format-time-string "%Y-%m-%d %H:%M:%S"))
(format (concat "<div class=\"creator\"> Generated with "
"<a href=\"https://www.gnu.org/software/emacs/\" target=\"_blank\" rel=\"noopener noreferrer\">Emacs %s</a>, "
"<a href=\"http://spacemacs.org\" target=\"_blank\" rel=\"noopener noreferrer\">Spacemacs %s</a>, "
"<a href=\"http://orgmode.org\" target=\"_blank\" rel=\"noopener noreferrer\">Org Mode %s</a>"
"</div>")
emacs-version spacemacs-version org-version)
"</footer>"
(menu '("<a href=\"#preamble\">↑ Top ↑</a>"))
"</div>"))
#+end_src
*** RSS
:PROPERTIES:
:CUSTOM_ID: rss-db16
:END:
#+begin_src elisp
("rss"
:base-directory ,rss-dir
:base-extension "org"
:html-link-home ,domainname
:html-link-use-abs-url t
:rss-extension "xml"
:publishing-directory ,publish-rss-dir
:publishing-function (org-rss-publish-to-rss)
:exclude ".*"
:include ("archive.org")
:section-numbers nil
:table-of-contents nil)
#+end_src
*** Obfuscate email
:PROPERTIES:
:CUSTOM_ID: obfuscate-email
:END:
#+begin_src elisp
(defun rand-obfs (c)
(let ((r (% (random) 20)))
(cond ;; ((eq 0 r) (format "%c" c))
((<= 0 r 10) (format "&#%d;" c))
(t (format "&#x%X;" c)))))
(defun obfuscate-html (txt)
(apply 'concat
(mapcar 'rand-obfs txt)))
#+end_src
*** Specific email subject
:PROPERTIES:
:CUSTOM_ID: specific-email-subject
:END:
#+begin_src elisp
(let* ((obfs-email (obfuscate-html email))
(obfs-author (obfuscate-html author))
(obfs-title (obfuscate-html (get-from-info info :title)))
(full-email (format "%s &lt;%s&gt;" obfs-author obfs-email)))
(format "<div class=\"author\">Author: <a href=\"%s%s%s%s\">%s</a></div>"
(obfuscate-html "mailto:")
full-email
(obfuscate-html "?subject=yblog: ")
obfs-title
full-email))
#+end_src
*** Nice external links
:PROPERTIES:
:CUSTOM_ID: nice-external-links
:END:
#+begin_src elisp
;; 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)
"Add target=\"_blank\" to external links."
(when (and
(org-export-derived-backend-p backend 'html)
(string-match "href=\"http[^\"]+" text)
(not (string-match "target=\"" text))
(not (string-match (concat "href=\"" domainname "[^\"]*") text)))
(string-match "<a " text)
(replace-match "<a target=\"_blank\" rel=\"noopener noreferrer\" " nil nil text)))
(add-to-list 'org-export-filter-link-functions
'my-org-export-add-target-blank-to-http-links)
#+end_src
*** Full code
:PROPERTIES:
:CUSTOM_ID: full-code
:END:
#+begin_src elisp
(setq domainname "https://her.esy.fun")
(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 "Yann Esposito")
(setq author-email "yann@esposito.host")
(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/"))
(defvar org-blog-head
(concat
"<link rel=\"stylesheet\" type=\"text/css\" href=\"" css-path "\"/>"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
"<link rel=\"alternative\" type=\"application/rss+xml\" title=\"" rss-title "\" href=\"/archives.xml\" />"
"<link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/favicon.ico\">"))
(defun menu (lst)
"Blog menu"
(concat
"<navigation>"
(mapconcat 'identity
(append
'("<a href=\"/index.html\">Home</a>"
"<a href=\"/archive.html\">Posts</a>"
"<a href=\"/about-me.html\">About</a>")
lst)
" | ")
"</navigation>"))
(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
"<div class=\"content\">"
(menu '("<a href=\"#postamble\">↓ bottom ↓</a>"))
"<h1>"
(format "%s" (car (plist-get info :title)))
"</h1>"
(when-let ((date (plist-get info :date)))
(format "<span class=\"article-date\">%s</span>"
(format-time-string "%Y-%m-%d"
(org-timestamp-to-time
(car date)))))
(when-let ((subtitle (car (plist-get info :subtitle))))
(format "<h2>%s</h2>" subtitle))
"</div>"))
(defun rand-obfs (c)
(let ((r (% (random) 20)))
(cond ;; ((eq 0 r) (format "%c" c))
((<= 0 r 10) (format "&#%d;" c))
(t (format "&#x%X;" c)))))
(defun obfuscate-html (txt)
(apply 'concat
(mapcar 'rand-obfs txt)))
(defun org-blog-postamble (info)
"Post-amble for whole blog."
(concat
"<div class=\"content\">"
;; TODO install a comment system
;; (let ((url (format "%s%s" domainname (replace-regexp-in-string base-dir "" (plist-get info :input-file)))))
;; (format "<a href=\"https://comments.esy.fun/slug/%s\">comment</a>"
;; (url-hexify-string url)))
"<footer>"
(when-let ((author (get-from-info info :author)))
(if-let ((email (plist-get info :email)))
(let* ((obfs-email (obfuscate-html email))
(obfs-author (obfuscate-html author))
(obfs-title (obfuscate-html (get-from-info info :title)))
(full-email (format "%s &lt;%s&gt;" obfs-author obfs-email)))
(format "<div class=\"author\">Author: <a href=\"%s%s%s%s\">%s</a></div>"
(obfuscate-html "mailto:")
full-email
(obfuscate-html "?subject=yblog: ")
obfs-title
full-email))
(format "<div class=\"author\">Author: %s</div>" author)))
(when-let ((date (plist-get info :date)))
(format "<div class=\"date\">Created: %s</div>"
(format-time-string "%Y-%m-%d"
(org-timestamp-to-time
(car date)))))
(when-let ((keywords (plist-get info :keywords)))
(format "<div class=\"keywords\">Keywords: <code>%s</code></div>" keywords))
(format "<div class=\"date\">Generated: %s</div>"
(format-time-string "%Y-%m-%d %H:%M:%S"))
(format (concat "<div class=\"creator\"> Generated with "
"<a href=\"https://www.gnu.org/software/emacs/\" target=\"_blank\" rel=\"noopener noreferrer\">Emacs %s</a>, "
"<a href=\"http://spacemacs.org\" target=\"_blank\" rel=\"noopener noreferrer\">Spacemacs %s</a>, "
"<a href=\"http://orgmode.org\" target=\"_blank\" rel=\"noopener noreferrer\">Org Mode %s</a>"
"</div>")
emacs-version spacemacs-version org-version)
"</footer>"
(menu '("<a href=\"#preamble\">↑ Top ↑</a>"))
"</div>"))
(defun org-blog-sitemap-format-entry (entry _style project)
"Return string for each ENTRY in PROJECT."
(when (s-starts-with-p "posts/" entry)
(format (concat "@@html:<span class=\"archive-item\">"
"<span class=\"archive-date\">@@ %s: @@html:</span>@@"
" [[file:%s][%s]]"
" @@html:</span>@@")
(format-time-string "%Y-%m-%d" (org-publish-find-date entry project))
entry
(org-publish-find-title entry project))))
(defun org-blog-sitemap-function (title list)
"Return sitemap using TITLE and LIST returned by `org-blog-sitemap-format-entry'."
(concat "#+TITLE: " title "\n"
"#+AUTHOR: " author-name "\n"
"#+EMAIL: " author-email "\n"
"\n#+begin_archive\n"
(mapconcat (lambda (li)
(format "@@html:<li>@@ %s @@html:</li>@@" (car li)))
(seq-filter #'car (cdr list))
"\n")
"\n#+end_archive\n"))
(defun org-blog-publish-to-html (plist filename pub-dir)
"Same as `org-html-publish-to-html' but modifies html before finishing."
(let ((file-path (org-html-publish-to-html plist filename pub-dir)))
(with-current-buffer (find-file-noselect file-path)
(goto-char (point-min))
(search-forward "<body>")
(insert (mapconcat 'identity
'("<input type=\"radio\" id=\"light\" name=\"theme\"/>"
"<input type=\"radio\" id=\"dark\" name=\"theme\"/>"
"<input type=\"radio\" id=\"raw\" name=\"theme\"/>"
"<input type=\"radio\" id=\"darkraw\" name=\"theme\"/>"
"<div id=\"labels\">"
"<div class=\"content\">"
"Change theme: "
"<label for=\"light\">Light</label>"
"(<label for=\"raw\">raw</label>)"
" / "
"<label for=\"dark\">Dark</label>"
"(<label for=\"darkraw\">raw</label>)"
"</div>"
"</div>"
"<div class=\"main\">")
"\n"))
(goto-char (point-max))
(search-backward "</body>")
(insert "\n</div>\n")
(save-buffer)
(kill-buffer))
file-path))
(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-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"))))
;; 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)
"Add target=\"_blank\" to external links."
(when (and
(org-export-derived-backend-p backend 'html)
(string-match "href=\"http[^\"]+" text)
(not (string-match "target=\"" text))
(not (string-match (concat "href=\"" domainname "[^\"]*") text)))
(string-match "<a " text)
(replace-match "<a target=\"_blank\" rel=\"noopener noreferrer\" " nil nil text)))
(add-to-list 'org-export-filter-link-functions
'my-org-export-add-target-blank-to-http-links)
#+end_src