CSS in org-publish
Since I am generating this site using org-mode
’s ox-html.el
exporter, I needed to understand how these generate CSS. By default the exporter opts for inline CSS styles generated from the current Emacs theme’s face definitions.
This makes the exported HTML self contained but ultimately the styling is just my current Emacs theme and not easily changeable.
ox-html.el
uses htmlize.el
to turn Emacs’ faces into CSS.
By setting org-html-htmlize-output-type
to 'css
instead of the default 'inline-css
, it will create CSS classes derived from the face name to style the HTML elements.
Calling org-html-htmlize-generate-css
will “produces a buffer that contains class definitions for all faces used in the current Emacs session.”
It’ll look something like this:
... .org-comment { /* font-lock-comment-face */ color: #2aa1ae; background-color: #292e34; } .org-constant { /* font-lock-constant-face */ color: #a45bad; } ...
But at over 4000 lines, its probably unwise to just throw this in a script tag and call it good.
In fact the docstring for org-html-htmlize-generate-css
even says “You can copy and paste the ones you need into your CSS file.”
But how should I know which ones I need without reading through all my exported html files?
Finding all Org CSS classes in HTML
Javascript is well suited to solving this.
The following snippet will get all the CSS classes with an org-
prefix used in an HTML page that are not present in the loaded stylesheets:
function unique(array) { var prev; return array.sort().filter(e => e !== prev && (prev = e)); } let org_classes = unique([].concat(...[...document.querySelectorAll('*')].map( elt => [...elt.classList]))).filter(e => e.startsWith('org-')); let classes = [...document.styleSheets].map( sheet => [...sheet.cssRules].map(rule => rule.selectorText)).flat(); org_classes.filter(org_class => classes.find( elt => elt === "."+org_class) === undefined);
Since I have several directories full of html, either I need to run this on every page (tedious), use node (gross)1, or do some clever command line foo:
rm $FNAME cat > /tmp/$FNAME <<EOL <!DOCTYPE html> <html lang="en"> <head> <!-- 2019-02-05 Tue 18:54 --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Akira Kyle</title> <meta name="generator" content="Org mode"> <link href='/styles.css' rel='stylesheet' type='text/css'> </head> <body> EOL find . -name "*.html" -exec sed '/^<body>$/,/^<\/body>$/{//!b};d' {} + >> /tmp/$FNAME cat >> /tmp/$FNAME <<EOL </body> </html> EOL mv /tmp/$FNAME $FNAME
You may be howling in despair at the sight of regexes being used to parse HTML. Let me put your soul at ease: gaze upon ox-html line 2077 and ox-html line 2121.2
Now that we have all our HTML files concatenated into a single large HTML file, we can run our javascript snippet to get all the org-
classes we’re missing in our stylesheet then copy them over from the org-html-htmlize-generate-css
buffer and modify them as we see fit.3
Footnotes:
If you really want node, checkout this post, but see how functional my js is compared to theirs.
I’m just running the js snippet in Firefox’s developer console