Writing themes

A Howl theme decides how the whole window looks: the colors and borders of the window and the boxes within it, the styles used for the text in the editors, and the flairs drawn on top of the text, such as the cursor, the selection and search highlights. A theme is a single CSS file, optionally accompanied by images, that is registered with Howl by a bundle. In this page we’ll go through how to write one, how to check it for mistakes, and how to contribute it to Howl.

Checking a theme for mistakes is done with a script from the Howl source, so you’ll want a checkout of the Howl repository at hand (see Howl development).

Getting started

The easiest way to get started is to copy one of the bundled themes and change it from there. The bundled themes live in the bundles/howl-themes directory of the Howl source, one directory per theme; the Monokai theme (bundles/howl-themes/monokai/monokai.css) is the shortest one.

Themes are registered by bundles, so your theme needs a bundle of its own. Your own bundles go in the bundles directory of your Howl user directory (~/.howl/ or ~/.config/howl/, see Init files). Create a directory for the bundle, named so that it doesn’t clash with any other bundle, and put your CSS file in it:

~/.howl/bundles/my-theme/
├── init.moon
└── my-theme.css

The bundle’s init.moon registers the theme, and unregisters it again when the bundle is unloaded:

{:theme} = howl.ui

theme.register 'My Theme', bundle_file('my-theme.css')

{
  info: {
    author: 'Your Name',
    description: 'My own theme',
    license: 'MIT',
  },
  unload: -> theme.unregister 'My Theme'
}

bundle_file resolves a path relative to the bundle’s directory, and all bundles must return the info fields and the unload function shown above. Once you restart Howl, the theme shows up among the others when you set the theme configuration variable (see Configuring Howl).

When you change the CSS file while the theme is in use, run the bundle-reload command and select your bundle (my_theme in this case). This registers the theme anew, which re-applies it with your changes. Setting the theme variable to the theme already in use does not re-read the file.

What’s in a theme file

A theme file is Gtk CSS, which styles the window and the widgets in it, plus a few extensions of Howl’s own. Howl processes these before handing the rest over to Gtk:

Comments (/* .. */) can go anywhere. A small but complete theme looks like this:

:root {
  --background: #1d1f21;
  --foreground: #c5c8c6;
  --comment: #969896;
  --purple: #b294bb;
  --green: #b5bd68;
}

/* The window, and the boxes holding the editors and the command line */
window {
  background-color: var(--background);
}

.content-box {
  background-color: var(--background);
}

.gutter {
  color: var(--comment);
}

/* Text styles */
style.default {
  color: var(--foreground);
}

style.comment {
  color: var(--comment);
  font-style: italic;
}

style.keyword {
  color: var(--purple);
  font-weight: bold;
}

style.string { color: var(--green); }

/* Flairs */
flair.cursor {
  shape: pipe;
  border-color: var(--foreground);
  width: 2;
  height: text;
}

flair.selection {
  shape: rounded-rectangle;
  background-color: alpha(var(--purple), 0.3);
  minimum-width: letter;
}

Using a variable that isn’t declared is an error that stops the whole theme from being applied, while other mistakes only affect the rule they’re in (see Checking a theme).

Styling the window

Everything outside of the text itself is styled with ordinary Gtk CSS, using the properties Gtk supports. These are the selectors for Howl’s parts of the window:

Selector What it is
window The main window. .main-window is set on it as well
window .container The area holding the views
.content-box The box around an editor, the command line and the activity box. .content-box-editor, .content-box-command_line and .content-box-activity target one of them
.content-box .header, .content-box .footer The indicator bars above and below an editor, and above the command line and activity box
.indicator A single indicator in these bars. The indicator’s id is set as a class too, so .indicator.title, .indicator.position, .indicator.processes, .indicator.inspections and .indicator.vi target one of them
.gutter The line number gutter
window .status The status messages. .info, .warning or .error is set along with it
popover, popover contents Popups, such as the completion list
scrollbar Scrollbars, e.g. scrollbar range trough slider

The background of an editor is that of its .content-box. The font family and size come from the font and font_size configuration variables, so themes shouldn’t set them for the window. Howl draws the line numbers in the gutter itself, using the color of the .gutter rule.

Text styles

A style rule sets the look of one kind of text, such as comments, keywords or strings. The selector must be a single style.<name>: grouped selectors, such as style.keyword, style.string, and any other combination are ignored. Dashes and underscores in names mean the same thing, so style.type-def and style.type_def are the same style. Several rules for the same style are combined.

These properties are supported:

Property Values
color The text color, as any CSS color. Must be opaque
background-color Any CSS color, including transparent ones such as #rrggbbaa or alpha(<color>, <0-1>)
font-style italic or normal
font-weight bold or normal (not numbers)
font-size A size in points (12 or 12pt), or one of xx-small, x-small, small, smaller, medium, large, larger, x-large and xx-large, relative to the editor’s font size
font-family A comma separated list of font families
text-decoration underline, line-through, both, or none

style.default is the base for all other styles: whatever a style doesn’t set is taken from style.default. A style that the theme doesn’t define is shown as the style it defaults to, if it has one, and as style.default otherwise. A theme therefore only needs to define the styles it wants to differ.

Below are all the styles used by Howl and its bundled modes, along with the style each one defaults to. The names are given as Howl’s code uses them, with underscores, but in a theme you can just as well write them with dashes, as the bundled themes do (style.type-def). The bundled themes define all of the code, document and diff styles except identifier, symbol, parameter, global and header, so they are a good place to start from.

Code

Style Used for Defaults to
comment Comments
keyword Keywords
string Strings
number Numbers
operator Operators and punctuation
identifier Other names, such as those of variables
constant Constants, such as upper case names
special Words and characters with a special meaning, such as true, self or string prefixes, depending on the language
type Type names
type_def Names of types being defined, such as in class declarations type
class Class names
fdecl Names of functions being defined
function Function names, such as those of built-in functions
key Keys, such as those in tables, hashes and objects
member Members, such as @name or self.name
variable Variables, in languages that mark them, such as $name in shell scripts
preproc Preprocessor directives, decorators and attributes
regex Regular expressions string
char Character literals
label Labels
tag Tags, such as those in XML
definition Definitions, such as Makefile targets
symbol Symbols, such as Ruby’s :name key
parameter Parameters key
global Global variables, such as Ruby’s $name member
error Invalid code
embedded Code embedded in other text, such as JavaScript in HTML or code in Markdown

Embedded code is shown with its own styles on top of embedded, so a background-color set for embedded shows behind all embedded code.

Documents

These are used for Markdown, mail and Cucumber files, and for the documentation Howl shows in popups.

Style Used for Defaults to
h1, h2, h3 Headings, and the subject in mail
emphasis Emphasized text: *text* and _text_ in Markdown, _text_ in mail italic
strong Strong text: **text** and __text__ in Markdown, *text* in mail, and titles in Cucumber files bold
link_label Link texts
link_url Link addresses
table Tables in Cucumber files

Diffs

Style Used for Defaults to
addition Added lines
deletion Removed lines
change Changed lines
header The --- and +++ lines naming the files comment

The user interface

Style Used for Defaults to
default All text, and the base for the other styles
popup Popups, if it sets a background-color default
info, warning, error Messages, such as in notifications and the journal
prompt The command line prompt keyword
command_name Command names keyword
keystroke Key bindings, in help texts special
directory, filename Directories and files, when selecting files key, string
list_header Column headers in lists grey, underlined
wrap_indicator The marker shown where a line wraps comment
blob Lines too complex to style, such as minified code preproc on top of embedded
black, red, green, yellow, blue, magenta, cyan, white Plain colors. The colored output of external commands uses the color of these Built-in colors
bold Bold text bold

Language specific styles

Some modes use styles of their own. All of them default to one of the styles above, except ANTLR’s action, which is plain text unless the theme defines it.

Style Language Defaults to
action ANTLR
builtInVariable, gawkBuiltInVariable, gawkKeyword, gawkNumber, gawkOperator, gawkRegex AWK constant, constant, keyword, number, operator, preproc
field AWK, BibTeX constant
entry BibTeX preproc
preprocessor C#, D, F#, Objective-C, Pike preproc
css_selector, css_property, css_unit, css_color, css_at, css_pseudo CSS keyword, key, type, string, preproc, class
gherkin_step, gherkin_placeholder, gherkin_description Cucumber symbol, preproc, string
annotation D, Java preproc
traits, versions D definition, constant
directive Erlang preproc
haml_element, haml_doctype, haml_id Haml keyword, special, constant
html_tag, html_attr, html_entity HTML keyword, key, preproc
jade_element, jade_id Jade keyword, constant
jsp_tag JSP embedded
environment, math, section LaTeX tag, function, class
mail_level_1, mail_level_2, mail_level_3, mail_level_4 Mail, quotes by level green, blue, cyan, comment
mail_ref, mail_link Mail bold, link_url
target Makefiles definition
color Properties files number
attribute, element, namespace, entity, doctype, cdata XML key, type, special, special, comment, comment

Flairs

Flairs are drawn on top of, or below, the text in the editors: the cursor, the selection, the current line, search matches, and so on. Like styles, a flair rule takes a single flair.<name> selector, and dashes and underscores in names are the same. A flair defined by the theme replaces Howl’s own definition entirely, so a flair rule has to set everything the flair needs.

Every flair needs a shape, which is one of:

Shape Draws
rectangle A box around the text, filled with background-color and outlined with border-color
rounded-rectangle The same, with rounded corners
sandwich A line above and below the text
underline A line below the text
wavy-underline A wavy line below the text
pipe A vertical line at the start of the text
strike-through A line through the text

A flair without a valid shape is ignored. The other properties are:

Property Values
border-color The color of the lines, or of the outline for rectangles. May be transparent
background-color The fill color for rectangles. May be transparent
color Redraws the text within the flair in this color. Must be opaque
border-style solid, dotted or dashed
border-radius The corner radius for rounded-rectangle, in pixels (3 or 3px)
width The width of the lines, in pixels. This is not the width of the flair
height The height in pixels, or text for the height of the text rather than the line
minimum-width The minimum width in pixels, or letter for the width of a character. Lets a flair show where it covers no text, such as the cursor or the selection at the end of a line

These are the flairs Howl uses:

Flair What it is
cursor The cursor
block-cursor The block cursor, used by the vi bundle’s command mode
inactive-cursor The cursor in editors that don’t have the focus
selection The selection
selection-overlay Drawn over the selection where the text has a background color
current-line The current line. It always spans the whole width of the editor, so it needs no width
current-line-overlay Drawn on the current line over text with a background color
indentation-guide The indentation guides. indentation-guide-1, indentation-guide-2, etc. override it for a given indentation level
edge-line The line marking the edge column
search, search-secondary The current search match, and the other matches
replace-strikeout Text about to be replaced, when previewing a replacement
brace-highlight The brace at the cursor, and its matching brace
brace-highlight-secondary The brace just before the cursor, and its matching brace
list-selection The selected item in lists
list-highlight The characters matching what you typed, in lists
list-visited Items already visited in list buffers, such as search results
error, warning Errors and warnings reported by inspections
stderr Error output of external commands

Howl has built-in definitions for most of these. brace-highlight, brace-highlight-secondary, replace-strikeout and list-highlight have none, and are only visible if the theme defines them.

Checking a theme

Mistakes in style and flair rules, such as an unknown property or an invalid value, are logged as Theme error: when the theme is applied, as are the CSS errors Gtk reports. The rest of the theme still applies. Within Howl, you can see the messages with the open-journal command.

It’s easier to use the howl-check-themes script in the bin/ directory of the Howl source, which applies themes without opening a window and lists the errors for each theme. Give it a theme file:

[howl-dir] $ ./bin/howl-check-themes ~/.howl/bundles/my-theme/my-theme.css
FAIL  /home/you/.howl/bundles/my-theme/my-theme.css
      unsupported selector 'style.keyword, style.string', ignoring the rule: style and flair rules take a single name
      style.comment: text colors can't be transparent ('alpha(#969896, 0.5)')
      style.comment: invalid font-weight '700'
      flair.cursor: no valid shape, ignoring it

A theme without mistakes is listed as ok, and the script exits with a non-zero status if any theme had errors. Gtk’s errors are shown along with the CSS surrounding them, where <ERROR> marks the spot. You can also give it the name of a theme; with --profile it loads your user directory, so that themes from your own bundles are included. Without arguments it checks all of the bundled themes:

[howl-dir] $ ./bin/howl-check-themes --profile 'My Theme'
[howl-dir] $ ./bin/howl-check-themes

A theme without errors can of course still look wrong, so switch to it and see for yourself.

Contributing a theme to Howl

To add a theme to the themes bundled with Howl, add a directory for it under bundles/howl-themes/ and register it in the list of themes in bundles/howl-themes/init.moon. Credits and copyright notes, e.g. for a theme based on another, go in bundles/howl-themes/README.md. The bundle’s spec applies every bundled theme and fails on any error, so make sure it passes:

[howl-dir] $ ./bin/howl-spec bundles/howl-themes/spec

The website has screenshots of every bundled theme, taken by the screen-shooter script. It’s also a quick way to see a bundled theme in use; this takes a screenshot with a few different views open, and writes it to /tmp/shots/my-theme/:

[howl-dir] $ ./bin/screen-shooter /tmp/shots 'My Theme' multi-views

To add the theme to the site, generate all of its screenshots into site/source/images/screenshots by leaving out the last argument, and add the theme to the list of screenshot pages in site/config.rb.


.. Back to the documentation index.