Introducing codetabs.el

Description

Recently, I've been trying to change the way I write blog posts. Up until now I was using Jekyll+Markdown to write and publish. However, I decided I didn't want Markdown anymore and instead use Org Mode (I use Emacs BTW!). The reason is very simple: CUSTOMIZATION.

If you read some of my previous blog post, I have code snippets that are grouped under tabs. This is because I often want to write multi-language code or simply have a beautiful code snippet and the name of a file as the tab name. Previous articles were relying on jdvp-codetabs-commonmark but I was missing some features and I already bothered the maintainer enough that I didn't feel like asking anymore. These features were:

  • Custom code highlighting (change color theme)
  • Code emphasis (emphasize errors and warnings in code)
  • Small animations (not important, but looks good)

So, I decided to implement this my way. During this process, I came up with codetabs.el and this article will demonstrate its features.

Behavior

Naming

With codetabs we can either get tabs named after the org babel language like so:

func main() {
}
int main() { return 0; }

or we can rename them with a simple #+name:

func main() {
}
int main() { return 0; }

For single src-blocks, it is similar:

ls

Skipping

We can skip some blocks. For example, the BQN block is consecutive but uses the attr :skip:

std::cout << "hello" << std::endl;
def test():
   print(1)
<'a'/ "Big Questions Notation"
std::cout << "world" << std::endl;
def test():
   print(2)

And it also works for single src-blocks:

ls

Copying

We can also enable copy of code with the :copy attribute (check the top right corner!):

func main() {
}

State

The language preference is reflected across the whole article. For example, if you click on C++ because you prefer reading in this language, all the C++ codes will be selected. Try it:

func main() {
}
int main() { return 0; }

SOME TEXT HERE….

func main() {
}
int main() { return 0; }

Styling

CSS

With org-html-htmlize-output-type set to css you can customize your CSS to make any token the color you want (see the function name difference in light and dark theme):

func main() {
}

This is just done like so:

.org-src-container pre .org-function-name {
  /* font-lock-function-name-face */
  color: #0000ff;
}
.org-src-container pre.dark-mode .org-function-name {
  /* font-lock-function-name-face */
  color: #0ff;
}

Emphasis

By using the :emphasize attr we can also define the styling for a given portion of code:

func main() {
  fmt.Printf("%d\n", ⟜"I pass a string"⊸)
  fmt.Printf(⊢"%d\n"⊣)
}

Misc

I customized the code snippets theme based on: tokyo-night-vscode-theme

I added the following animations:

  • Switching to/from dark mode
  • Tab transitions
Written on May 26, 2025