Check the output in ox-tailwind GitHub docs.
This back-end has the purpose of allowing easy customization of the HTML
output. Although it is called Tailwind
, the only thing that it does is
allowing you to customize the classes of the HTML and exporting a more
barebones HTML (It does not create as many divs
and sections
as the normal
HTML export back-end). Instead of using Tailwind.css you can just name the
classes of the elements and import your own css (or edit ./css/style.css
).
Note: Although you can export any .org
file as html by using C-c C-e x
h
(through org-export-dispatcher
), this method won’t create the file inside
the dist
folder where you have all the required js
and css
files. If you
open this file on a browser and open the console you will notice there are
import errors. You should instead setup your notes directory with
org-publish-project-alist
and then publish
the project using
(org-publish-all)
or (org-publish-all t)
to forcefully publish files.
To install this back-end, clone this repo into your packages or private folder in your ~~/.emacs.d~ directory:
git clone https://github.com/vascoferreira25/ox-tailwind
(out)Cloning into '~/.emacs.d/private'...
(out)remote: Enumerating objects: 49, done.
(out)remote: Counting objects: 100% (49/49), done.
(out)remote: Compressing objects: 100% (40/40), done.
(out)remote: Total 49 (delta 20), reused 28 (delta 8), pack-reused 0
(out)Unpacking objects: 100% (49/49), done.
And add the following to your config file:
;; ox-tailwind export back-end
(load "~/.emacs.d/private-packages/ox-tailwind/ox-tailwind.el")
Afterwards, copy the notes_example
folder to get an already made directory
structure for your notes including all the required CSS and JS files. You just
have to setup org-publish-project-alist
and then use the (org-publish-all)
command and you are done. Check the /notes_example/index.org
file.
If you use straight
as your package management, add this to your config file:
(straight-use-package
'(ox-tailwind :type git :host github :repo "vascoferreira25/ox-tailwind"))
To use this package with Doom Emacs, add the package to packages.el
:
(package! ox-tailwind
:recipe (:host github :repo "vascoferreira25/ox-tailwind"))
And then, load it after ox-html
(after! ox-html (require 'ox-tailwind))
If you use org-roam
and want to show the backlinks of the current file, add
this function to your config:
;; Add backlinks to the export
(defun collect-backlinks-string (backend)
(when (org-roam-node-at-point)
(let* ((source-node (org-roam-node-at-point))
(source-file (org-roam-node-file source-node))
;; Sort the nodes by the point to avoid errors when inserting the
;; references
(nodes-in-file (--sort (< (org-roam-node-point it)
(org-roam-node-point other))
(-filter (lambda (node)
(s-equals?
(org-roam-node-file node)
source-file))
(org-roam-node-list))))
;; Nodes don't store the last position so, get the next node position
;; and subtract one character
(nodes-start-position (-map (lambda (node) (org-roam-node-point node))
nodes-in-file))
(nodes-end-position (-concat (-map (lambda (next-node-position)
(- next-node-position 1))
(-drop 1 nodes-start-position))
(list (point-max))))
;; Keep track of the current-node index
(current-node 0)
;; Keep track of the amount of text added
(character-count 0))
(when (and nodes-in-file (s-equals? backend "latex"))
(insert "\n\n\\clearpage\n\n"))
(dolist (node nodes-in-file)
(when (org-roam-backlinks-get node)
;; Go to the end of the node and don't forget about previously inserted
;; text
(goto-char (+ (nth current-node nodes-end-position) character-count))
;; Add the references as a subtree of the node
(setq heading (format "\n\n%s Backlinks\n"
(s-repeat (+ (org-roam-node-level node) 1) "*")))
;; Count the characters and count the new lines (4)
(setq character-count (+ 3 character-count (string-width heading)))
(insert heading)
;; Insert properties drawer
(setq properties-drawer ":PROPERTIES:\n:HTML_CONTAINER_CLASS: references\n:END:\n")
;; Count the characters and count the new lines (3)
(setq character-count (+ 3 character-count (string-width properties-drawer)))
(insert properties-drawer)
(dolist (backlink (org-roam-backlinks-get node))
(let* ((source-node (org-roam-backlink-source-node backlink))
(point (org-roam-backlink-point backlink))
(text (s-replace "\n" " " (org-roam-preview-get-contents
(org-roam-node-file source-node)
point)))
(references (format "- [[./%s][%s]]: %s\n\n"
(file-relative-name (org-roam-node-file source-node))
(org-roam-node-title source-node)
text)))
;; Also count the new lines (2)
(setq character-count (+ 2 character-count (string-width references)))
(insert references))))
(setq current-node (+ current-node 1))))))
(add-hook 'org-export-before-processing-hook 'collect-backlinks-string)
In order for the search bar to work, this export backend will search all .org
files in the path of your project and store all the headings in a .js
file.
The folder structure for your notes should be like this:
tree /F /a
(out)C:/path/to/your_notes/
(out)| index.org
(out)| ... other org files
(out)|
(out)+---files
(out)| # Your attachments
(out)|
(out)+---img
(out)| # Your image files
(out)|
(out)+---dist
(out) # Org Publish Files
The home page for your notes should be index.org
.
For easier search and note management, the notes should be named as
<subject>_<notes_name>_<guide|snippets|templates|...>.org
.
Examples:
Notes | File Name |
---|---|
Programming Guides Index | programming.org |
Python notes Index | programming_python.org |
Python guide | programming_python_guide.org |
Python snippets | programming_python_snippets.org |
Guides Index | guides.org |
How to manage Ebooks guide | guides_manage_ebooks.org |
Gaming Index | gaming.org |
Skyrim guide | gaming_skyrim.org |
Subjects Index | subjects.org |
Mathematics | subjects_mathematics.org |
Economics | subjects_economics.org |
Index files should have the following template
* Subjects
** Pages
[[./subjects_accounting_and_finance.org][Accounting and Finance]]
[[./subjects_computer_science.org][Computer Science]]
[[./subjects_economics.org][Economics]]
[[./subjects_elo_rating.org][Elo Rating System]]
[[./subjects_mathematics.org][Mathematics]]
[[./subjects_statistics.org][Statistics]]
** References
To setup automatic export of all my org files I use the following settings:
(setq org-publish-project-alist
'(("org-files"
:base-extension "org"
:base-directory "V:/orgmode/"
:publishing-directory "V:/orgmode/dist/"
;; or use `org-tailwind-publish-to-html' to generate the toc after each
;; file - *note*: it will be slower to parse the whole project
:publishing-function org-tailwind-publish-to-html-without-toc)
("images"
:base-directory "V:/orgmode/img/"
:base-extension ".*"
:publishing-directory "V:/orgmode/dist/img/"
:publishing-function org-publish-attachment)
("files"
:base-directory "V:/orgmode/files/"
:base-extension ".*"
:publishing-directory "V:/orgmode/dist/files/"
:publishing-function org-publish-attachment)
("tangles"
:base-directory "V:/orgmode/tangles/"
:base-extension ".*"
:publishing-directory "V:/orgmode/dist/tangles/"
:publishing-function org-publish-attachment)
;; Publish all in one time
("notes" :components ("org-files" "images" "files" "tangles"))))
After setting up your notes path, you should use (org-publish-all)
to publish
all the notes as html.
**Note**: Before publishing, open a buffer on one of your .org
files or just
dired
into the notes directory. As this back-end needs to create a .js
file
based on your .org
files to enable searching, if the Emacs current directory
isn’t in the notes directory, it will fail to create this file.
In order to be faster to parse all your notes, it is advisable to only generate
the toc file after publishing. Use the following functions instead of the
org-export-dispatch
to automatically generate the toc after publishing:
(defun publish-file-and-build-toc ()
"Force publish the current org-mode file."
(interactive)
(org-publish-current-file)
(org-tailwind-build-toc))
(defun force-publish-file-and-build-toc ()
"Force publish the current org-mode file."
(interactive)
(org-publish-current-file t)
(org-tailwind-build-toc))
(defun publish-all-and-build-toc ()
"Force publish all org-mode files."
(interactive)
(org-publish-all)
(org-tailwind-build-toc))
(defun force-publish-all-and-build-toc ()
"Force publish all org-mode files."
(interactive)
(org-publish-all t)
(org-tailwind-build-toc))
In order for the export to work, you need to put the required files in the
output folder. Just copy the /notes_example/dist
folder into your notes
/dist/
folder.
This is the directory structure of the export folder:
tree /F /a
(out)C:/path/to/your_notes/dist/
(out)| # The HTML export
(out)| index.html
(out)|
(out)+---css
(out)| prism.css
(out)| style.css # Your css file
(out)| tailwind.min.css
(out)|
(out)+---files
(out)| # Your attachments
(out)+---img
(out)| # Your image files
(out)| spacemacs_1.png
(out)| spacemacs_2.png
(out)|
(out)+---js
(out)| clipboard.min.js
(out)| mermaid.min.js
(out)| polyfill.min.js
(out)| prism.js
(out)| tex-mml-chtml.js
(out)| toc_tree.js
(out)|
(out)+---mathjax
(out) # Mathjax Files
When publishing your org files, Org-Mode won’t delete any files in the /dist/
folder. If you delete org files and don’t delete those files from the /dist/
folder, you will end up with obsolete html files. In this case, what you should
is delete all the html files and then use (org-publish-all t)
to force
publish all your org files again.
Also, if you delete images, tangles or other files from /your_notes/files
,
/your_notes/tangles
or /your_notes/img
there will be a copy of them in the
/dist/
folder.
To completely clean the /dist/
folder you can delete all the following files
and folders:
/dist/files
,/dist/img
,/dist/tangles
,- all
.html
files.
By default, the file-name on the top of the page is a link for org-protocol
.
You can customize the link by changing the variable org-tailwind-file-name-link
or you can disable the link by changing the variable
org-tailwind-file-name-use-link
to nil.
Setup the org-protocol.
(server-start)
(require 'org-protocol)
;; Custom protocols
(defun my-open-file-protocol-handler (data)
(let* ((file-link (plist-get data :file))
(file-path (replace-regexp-in-string "^\\(.*\\)#.*" "\\1" file-link)))
(message "file-path: %s" file-path)
(find-file file-path))
;; it must end in nil or it will create a buffer
nil)
(setq org-protocol-protocol-alist
'(("open-file"
:protocol "open-file"
:function my-open-file-protocol-handler)))
Define a new link type for protocols and the export method:
(require 'ol)
(org-link-set-parameters "org-protocol"
:follow #'my-org-protocol-open
:export #'my-org-protocol-export
:store #'my-org-protocol-store-link)
(defun my-org-protocol-open (path _)
"Visit the org-protocol on PATH.
PATH should be a topic that can be thrown at the man command."
(browse-url path))
(defun my-org-protocol-store-link ()
"Store a link to a man page."
(when (memq major-mode '(org-mode))
;; This is a man page, we do make this link.
(let* ((file (my-org-protocol-get-file-name))
(link (concat "" file))
(description (format "org-protocol for %s" file)))
(org-link-store-props
:type "org-protocol"
:link link
:description description))))
(defun my-org-protocol-get-page-name ()
"Extract the file name from the buffer name."
(if (string-match " \\(\\S-+\\)\\*" (buffer-name))
(match-string 1 (buffer-name))
(error "Cannot create link to this org-protocol file")))
(defun my-org-protocol-export (link description format _)
"Export a man page link from Org files."
(let ((path (format "org-protocol:%s" link))
(desc (or description link)))
(pcase format
(`html (format "<a href=\"%s\">%s</a>" path desc))
(`latex (format "\\href{%s}{%s}" path desc))
(`texinfo (format "@uref{%s,%s}" path desc))
(`ascii (format "%s (%s)" desc path))
(t path))))
Export links to files with a link to open those files in Emacs through the protocol:
(defun my-open-in-emacs-link (backend)
"Add a link to use org-protocol to open a file in emacs."
(save-excursion
(goto-char (point-min))
(while (re-search-forward "\\(\\[\\[file:.*?\\]\\[.*\\]\\]\\)" nil t)
(message "MATCHED: %s" (match-string-no-properties 1))
(let* ((file-path (replace-regexp-in-string
".*\\[\\[file:\\(.*?\\)\\]\\[.*"
"\\1"
(match-string-no-properties 1)))
(fixed-file-path (replace-regexp-in-string
"\\(.*?\\)\\(::.*\\)?"
"\\1"
file-path)))
(message "FILE_PATH: %s" fixed-file-path)
(replace-match
(format "%s ([[org-protocol://open-file?file=%s][open in Emacs]])"
(match-string-no-properties 1)
fixed-file-path))))))
(add-hook 'org-export-before-processing-hook 'my-open-in-emacs-link)
To customize the theme you have to change the org-tailwind-class-...
variables.
There are multiple classes for all the Html tags. For example, changing the
theme of a h1
tag:
(defcustom org-tailwind-class-h1
"mt-24 mb-6 text-3xl text-gray-700 dark:text-gray-400 border-b \
hover:text-blue-400 dark:hover:text-blue-500 border-gray-500")
You can check all the other Html elements in the ox-tailwind.el
file.
If you want to change TailwindCSS settings you can use the TailwindCSS - CLI
tool. In the folder tailwind
you can change the tailwind.config.js
and
build the css
file. Then you just need to copy the output file to the
/dist/css
folder.
To create a config file you need to run:
cd ./tailwindcss
npx tailwindcss-cli@latest init
To build the css file run:
npx tailwindcss-cli@latest build -o ./tailwindcss/tailwind.css
To customize the code blocks, you can just download another theme from the
Prism.js website and save both the js
and the css
file in your /dist
folder. The link already has all the options selected, just select the theme
you want and download both the js
and css
files and save them into the dist
folder.
These are the required modules for Prism to work:
- line highlight
- line numbers
- autolinker
- file highlight
- show language
- jsonp highlight
- inline color
- previewers
- autoloader
- keep markup
- command-line
- unescaped markup
- normalize whitespace
- data-uri highlight
- toolbar
- copy to clipboard button
- download button
- match braces
- diff highlight
- filter highlight all
- treeview
Mathjax has been downloaded from source by running:
git clone https://github.com/mathjax/MathJax.git mathjax
And then copy the files from /mathjax/es5
into the /dist/mathjax
folder.
Mermaid has been downloaded from source by running:
git clone https://github.com/mermaid-js/mermaid.git
And then copy the files from /mermaid/dist
into the /dist/js
folder.
Bold Text
Italic Text
Underlined Text
Strike Through
Verbatim
Inline code
- Item number 1
- Item number 1.1
- Item number 1.2
- Item number 1.3
- Item number 2
- Item number 3
- Item number 4
- Item number 5
- Like
- This
- One
- This
- Tip Blocks
- Are for displaying tips.
- Warning Blocks
- Are for displaying warnings.
- Danger Blocks
- Are for displaying dangers.
- [ ] Unchecked 1
- [ ] Unchecked 2
- [X] Checked 1
A | B | C |
---|---|---|
<l> | <c> | <r> |
In this column | In this | Finally, |
the text | column | in this one |
is left aligned | it is centered | it is right aligned |
Inline formulas: $∑i=0^n i^2 = \frac{(n^2+n)(2n+1)}{6}$
Once upon a time..........
Source code blocks can be downloaded directly from github:
This uses the following attributes:
#+ATTR_FILENAME: core.cljs
#+ATTR_HIGHLIGHT: 2,6-8,11-20,48-51
#+ATTR_FETCH: https://api.github.com/repos/vascoferreira25/discord-bot/contents/src/main/core.cljs
There are four custom blocks: details
, tip
, warning
and danger
and
these blocks can contain other elements. In order to get syntax highlighting
while editing in Emacs, use org
as language.
Cool! Isn’t it?
There are also mermaids.
The following blocks have custom attributes that you can change:
- Source code
-
#+ATTR_HIGHLIGHT
- lines to highlight in the source code, e.g.
1,5-10,12
#+ATTR_USERNAME
- username to show in command-line blocks, e.g.
CrazyCat
#+ATTR_HOSTNAME
- hostname to show in command-line blocks, e.g.
localhost
#+ATTR_FETCH
- fetch files from the Github API
#+ATTR_FILEPATH
- get files and add a download button, it uses HTTP so, no local files.
#+ATTR_FILENAME
- name to display on the source code window.
- Custom blocks
-
#+NAME
- the title of the block
- Tables
-
#+NAME
- the description of the table
- Images
-
#+NAME
- the description of the image
- Videos
-
#+NAME
- the description of the video
#+ATTR_TIMELINE
- the time of the start and/or end of the video, for
example:
#+ATTR_TIMELINE: 5
#+ATTR_TIMELINE: 5,9
- Blockquotes
-
#+NAME
- the name of the author
- It crashes when it encounters a line that ends in
\\
- it works if it is inside a block; - It won’t export
TODO
keywords andSCHEDULE
dates.