← Otto User GuideTheming

Otto’s look is set entirely from the config file. There is no theme picker GUI.

Light and dark

theme_scheme = "Light"   # or "Dark"

This drives the compositor’s own chrome (the dock, app switcher, exposé, workspace selector) and is also exported to applications through the XDG Desktop Portal Settings interface as org.freedesktop.appearance color-scheme.

That means GTK apps, Firefox, Chromium, Electron apps and the top bar all follow this one setting, and switch within about a second when you change it. No restart needed for clients that listen for the change signal.

For this to work, the Otto portal backend must be selected for the Settings interface. See Screen Sharing, which covers the same portals.conf.

# gtk_theme = "Adwaita"

gtk_theme is recorded for reference only; Otto does not apply it. Set your GTK theme the usual way (gsettings, ~/.config/gtk-3.0/settings.ini).

Accent colour

accent_color = "blue"

The accent tints selection borders in the workspace selector, the exposé window highlight, and the controls in Otto’s own apps: toggles, sliders, focus rings and selected rows. Applications outside Otto can follow it too: it is published as accent-color in the org.freedesktop.appearance portal namespace.

Changing it takes effect immediately; no restart is needed. Available values:

red, orange, yellow, green, mint, teal, cyan, blue, indigo, purple, pink, gray, brown

A named accent follows the light and dark palettes, shifting to the right shade for the current scheme. You can also give a hex literal for a colour the palette has no name for:

accent_color = "#7fd4e6"

#RGB and #RRGGBB are both accepted, and the # is required — it is what tells a colour from a name. A literal is used exactly as written in both schemes.

Rounded corners

rounded_corners = true

Rounds the corners of the dock, the top bar, Otto-drawn window decorations and the desktop’s own panels. Set it to false for square corners throughout. It takes effect immediately: Otto repaints its own chrome, and the applications drawing their own (the top bar, and anything built on otto-kit) are told through the portal’s org.otto.desktop rounded-corners, the same door color-scheme comes through.

Frosting

frosting = true

The translucent, blurred material behind the dock, the top bar, the launcher, the app switcher, the exposé labels, the OSD and the selectors. Set it to false and the same surfaces are drawn nearly opaque instead, and Otto stops blurring the desktop for them, which also saves the work of keeping those blurs fresh under a playing video. Menus are the most solid, since they open over whatever is on screen. The dock and the top bar let a little more of the wallpaper through, since nothing else passes under them. It takes effect immediately, by the same route as the corners: Otto repaints its own chrome, and the bar and the launcher are told through the portal’s org.otto.desktop frosting.

Wallpaper

background_image = "/usr/share/otto/wallpaper.jpg"
background_color = "#102E95"
background_mode = "fill"
background_position_x = 0.5
background_position_y = 0.5
background_zoom = 1.0

Otto’s packages ship a wallpaper at /usr/share/otto/wallpaper.jpg, and the default configuration uses it. Point background_image at any other image to replace it: JPEG, PNG, WebP, GIF, BMP or SVG. A camera’s rotation tag and an embedded colour profile are honoured.

background_color is shown when there is no image, or it fails to load, as a gradient with this as the bottom colour. It is also what shows around a fitted or centred image and through a transparent one.

background_mode decides how the image fills the screen:

ModeWhat it does
fillScaled to cover the screen, the excess cropped. The default.
fitScaled to fit inside the screen, whole, with the colour around it.
stretchStretched to the screen’s shape, ignoring the image’s own.
centerAt its own size, with the colour around it, or cropped if larger.
tileRepeated at its own size from the top-left corner.
spanOne image across all your displays, as if they were one; each shows its part.

In center and tile an image pixel is a point, so the image is the same size on a HiDPI display as on a normal one.

background_position_x and background_position_y pick which part of a cropped image shows, and where a smaller one sits, as fractions across and down, the way CSS object-position does: 0.5 centres, 0.0 keeps the left or top edge, 1.0 the right or bottom. A portrait whose subject is near the top stays in view with background_position_y = 0.2. In the Settings app, click the part of the preview you want to keep.

background_zoom (1.0 to 8.0) magnifies a fill or span image beyond just covering the screen; the position then pans it.

The wallpaper is resampled once for each display, at that display’s resolution, and drawn pixel for pixel, so it is as sharp as the image allows and stays still however the screen around a moving window is redrawn.

One display, or one workspace

Any of these settings can be set for one display, and for one workspace:

[background_outputs."DP-1"]
image = "/home/me/Pictures/ultrawide.jpg"
mode = "fit"
color = "#000000"

[workspaces.entries."eDP-1:2".background]
image = "/home/me/Pictures/focus.png"

The names are those of the global settings without background_. A workspace takes the global settings, then its display’s, then its own, one setting at a time. image = "" means no image. The key "eDP-1:2" is the display and the workspace’s position counting from 0; a workspace’s wallpaper moves with it when you reorder workspaces.

Both are easier set with otto-msg:

otto-msg 'output DP-1 bg ~/Pictures/ultrawide.jpg fit #000000'
otto-msg 'background ~/Pictures/focus.png'     # the focused workspace
otto-msg 'output DP-1 bg reset'

Otto also supports the wlr-layer-shell background layer, so wallpaper daemons like swaybg and swww work if you want animated or per-monitor backgrounds:

[[exec_once]]
cmd = "swaybg"
args = ["-i", "/path/to/wallpaper.jpg", "-m", "fill"]

Fonts

font_family = "Inter"

The font used by Otto’s own UI: dock labels, exposé window titles, the app switcher. It is also offered to applications as the interface font, through the desktop portal (GTK 4), GNOME’s settings store (GTK 3, and Qt through its GTK platform theme) and XSETTINGS (X11 apps), along with your fontconfig antialiasing and hinting; an application’s own font preference, where it has one, still wins. Changing it takes effect after restarting Otto.

Otto ships with Inter as its default. Any font installed on the system works.

Cursor

cursor_theme = "Otto-MacTahoe"
cursor_size = 24

The icon theme Otto ships, Otto-MacTahoe, carries its own cursors, so the default configuration uses it for both.

Cursor theme names are the directory names under /usr/share/icons/ and ~/.local/share/icons/, and they are case-sensitive. Check what you have:

ls /usr/share/icons ~/.local/share/icons

Otto also implements wp-cursor-shape-v1, so applications that use the modern cursor-shape protocol get themed cursors without shipping their own bitmaps.

Icon theme

icon_theme = "Otto-MacTahoe"

Used for application icons in the dock, the app switcher and notification islands. Commented out or absent, Otto auto-detects a reasonable theme from what is installed.

Otto’s packages ship the MacTahoe icon theme under Otto’s own name — Otto-MacTahoe, Otto-MacTahoe-light and Otto-MacTahoe-dark — and the default configuration uses it, so a first install has a complete set of icons. The separate name keeps it from clashing with a MacTahoe you install yourself, which you can select by its own name, MacTahoe. Any other theme under /usr/share/icons works the same way.

The theme draws apps, files and tray icons. The glyphs on the controls of Otto’s own apps (toolbar buttons, menu ticks, the sidebars of Files and Settings) are Otto’s own, from the Solar icon set by 480 Design, and look the same whatever theme you choose.

Popular choices: Otto-MacTahoe, MacTahoe, WhiteSur, Adwaita, Papirus, Fluent. Otto’s own screenshots use the Fluent icon theme.

Icons are looked up per the freedesktop icon theme specification, including inheritance: a theme that lacks an icon falls back to its parent.

Language

locales = ["en_US", "en"]

Preferred languages for application names and descriptions read from .desktop files. Use standard locale identifiers, most specific first, and prefer the region-qualified form (lang_COUNTRY): ["fr_FR", "fr"] means “French (France) if the entry has that variant, plain French otherwise, English as the ultimate fallback from the entry’s untranslated name”. Matching follows the freedesktop Desktop Entry spec and falls back automatically from region to bare language, so listing both zh_CN and zh catches entries that localize either form.

This affects names shown in the dock, app switcher and menus, and it is also Otto’s own UI language: the bar, settings, Files and the other bundled apps read the same list and pick a translation from it. Otto ships catalogues for German, English (GB and US), Spanish, French, Italian, Japanese, Polish, Portuguese (Brazil), Russian, Ukrainian and Simplified Chinese; anything else falls back to English.

Looks

A look is a set of wallpaper, icon theme, cursor theme and settings (dock position and tint included) that belong together. otto-look installs one and applies it to the running session:

otto-look list                 # the published looks
otto-look ember                # the latest version of Ember
otto-look ember@1              # a given version
otto-look ./my-look            # a look of your own: a folder holding look.toml,
otto-look ./my-look.tar.gz     # that folder packed,
otto-look https://example.org/looks/mine/look.toml   # or either one online

Published looks come from nongio/otto-looks, each one a package checked against its digest. The wallpaper goes in ~/.local/share/backgrounds/otto-looks/, and the settings apply at once and survive a restart. A theme that is not installed yet is downloaded from its makers, checked against the sha256 in the look, and installed for you alone, in ~/.local/share/icons. otto-look never uses sudo. It needs curl, tar, gzip, sha256sum and busctl, plus unzip for a theme shipped as a zip, and prints the credits for each piece when it’s done.

A worked example

A dark HiDPI setup:

screen_scale = 2.0

theme_scheme = "Dark"
accent_color = "purple"
font_family = "Inter"

background_image = "/home/me/Pictures/wallpaper.jpg"
background_color = "#101018"

cursor_theme = "Adwaita"
cursor_size = 32
icon_theme = "Papirus-Dark"

locales = ["en_US", "en"]

Troubleshooting

Icon or cursor theme not found. Names are case-sensitive and must match the directory name exactly. Check with ls /usr/share/icons/. Some themes need a separate package for their cursor variant.

Applications ignore light/dark. GTK 4 apps, Firefox and Chromium read it from the XDG Settings portal, which needs xdg-desktop-portal running with Otto selected for org.freedesktop.impl.portal.Settings in your portals.conf. GTK 3, libadwaita and Qt apps read GNOME’s settings store instead, which Otto’s own session writes; check it with gsettings get org.gnome.desktop.interface color-scheme, which should say 'prefer-dark' or 'default'. For the portal, verify with:

gdbus call --session --dest org.freedesktop.portal.Desktop \
  --object-path /org/freedesktop/portal/desktop \
  --method org.freedesktop.portal.Settings.Read \
  org.freedesktop.appearance color-scheme

1 means dark, 2 means light, 0 means no preference.

The wallpaper is a gradient instead of my image. The path is wrong or unreadable, or the file is not an image Otto can decode. Check the log. Otto reports the failure (the Settings app and otto-msg say why) and falls back to background_color. An override in [background_outputs] or a workspace’s background wins over the global setting, so check those too.

Not yet supported