It took me a lot of debugging to figure this out. I started inspecting the values of the six arguments, and I noticed something weird was going on with the text variable.

#( "Introduction" 0 12 ( :parent (headline ( :raw-value "Introduction" :begin 105 :end 507 :pre-blank 0 :contents-begin 121 :contents-end 506 ...

The variable contains the text of the headline, but apparently also some other things.

I tried to inspect the type of the variable:

(type-of text)

string

But that didn’t help me much…

I could not understand what #( meant. So I started searching the web and eventually I stumbled on a StackOverflow answer. It turns out, and I really didn’t know that, strings in Emacs lisp can have Text Properties.

Interesting…

In the example above, there is a text property :parent from character 0 with length 12 .

Apparently, this property contains the headline element. And Org mode has functions to read properties of that element. All what remained to do was extract the :CUSTOM_ID: or :ID property and wrap the text in <a href...> & </a> tags.

Eventually I ended up with this function:

( defun my-org-html-format-headline-function (todo todo-type priority text tags info) "Format a headline with a link to itself." ( let* ((headline (get-text-property 0 :parent text)) (id ( or (org-element-property :CUSTOM_ID headline) (org-export-get-reference headline info) (org-element-property :ID headline))) (link ( if id (format "<a href=\"#%s\">%s</a>" id text) text))) (org-html-format-headline-default-function todo todo-type priority link tags info)))

You can find the exact function I’m using here.

And then all you need to do is specify this function in your org-publish-project-alist .

( setq org-publish-project-alist (list (list "blog-posts" :base-directory "posts" :base-extension "org" :html-format-headline-function 'my-org-html-format-headline-function ) ))

Comments are welcome on Reddit.