Skip to content
docs
GitHub

Resources and CSS

Add local images, stylesheets, fonts and plugin tokens to a theme package.

Use a package folder when a theme needs resources beyond theme.jsonc. The complete Graphite Grid example contains:

graphite-grid/
theme.jsonc
theme.css
images/
grid.svg
{
"version": 2,
"name": "Graphite Grid",
"appearance": "adaptive",
"common": {
"backgrounds": {
"terminal": {
"image": "images/grid.svg",
"opacity": 0.08,
"size": "32px 32px",
"repeat": "repeat"
}
},
"plugins": {
"example.workspace-info": { "surface": "var(--color-surface)" }
}
},
"resources": {
"stylesheets": ["theme.css"],
"assets": { "grid": "images/grid.svg" }
}
}

backgrounds.terminal.image is a package-relative path, not an asset alias. resources.assets.grid also declares the same image as a named resource. It makes the image available to CSS as --asset-grid and includes it in resource readiness checks.

The terminal image uses a separate background layer. Its opacity does not fade terminal text.

.workspace-info {
border: 1px solid var(--color-outline);
border-radius: var(--radius-control);
background-color: var(--plugin-example-dworkspace-hinfo-surface, var(--color-surface));
background-image: var(--asset-grid);
}

This stylesheet decorates the workspace-info example. It uses existing semantic variables and a plugin token with a fallback. The --asset-grid value already contains url(...), so use var(--asset-grid) directly.

Stylesheets apply in the order listed in resources.stylesheets. URLs written inside a stylesheet resolve relative to that stylesheet, including @import paths. A URL in css/theme.css pointing at a sibling image directory would therefore use ../images/grid.svg, while paths in the JSONC manifest remain relative to the package root.

Place local font files inside the package. Declare required fonts in resources.assets and reference them from CSS using @font-face:

@font-face {
font-family: "My Mono";
src: url("fonts/my-mono.woff2") format("woff2");
font-weight: 400;
font-style: normal;
}

This example assumes theme.css at the package root and an existing fonts/my-mono.woff2 file. Add the corresponding asset declaration, then set editor.fontFamily, terminal.fontFamily or a suitable font token to "My Mono", monospace. Include a font you have permission to distribute.

Remote resource URLs are blocked by the application’s content security policy. A nested CSS import that fails is not always reported by the outer stylesheet’s load event, so declare required images and fonts explicitly.

The example’s common.plugins declaration creates:

--plugin-example-dworkspace-hinfo-surface: var(--color-surface);

The name combines --plugin-, the encoded owner and the token name. Encode - as -h, then . as -d. This keeps different plugin owner IDs distinct. Plugin token names begin with a lower-case letter and use lower-case letters, digits or hyphens.

For color themes, the effective order is:

  1. Built-in baseline for the resolved appearance.
  2. Imported VS Code color mapping, if present.
  3. common values.
  4. The matching light or dark values.
  5. Local stylesheets in manifest order, subject to the CSS cascade.
  6. User terminal appearance overrides.

Deleting an override restores the inherited value. Terminal user overrides are applied with inline !important values. CSS can also override public syntax variables named --syntax-<name>-color, -font-style, -font-weight and -text-decoration.

CSS can hide or break application controls. It is not a security boundary. Keep changes scoped and test both appearances before distributing a package.

Keep resources inside the package. Symlinks, traversal, absolute paths, encoded separators, Windows reserved names and alternate streams are rejected. A native theme package is limited to 64 MiB, 8192 filesystem entries, 16 directory levels and 20 MiB per asset. The theme manifest has its own 256 KiB limit.

The runtime prepares required resources before replacing the working appearance. A missing asset or failed required stylesheet keeps the last working theme active and reports an error.

Supported files and a complete font declaration

Section titled “Supported files and a complete font declaration”

The resource protocol serves CSS; PNG, JPG/JPEG, WebP, GIF, SVG, AVIF and ICO images; and WOFF, WOFF2, TTF and OTF fonts. A required stylesheet must be UTF-8 and no larger than 256 KiB, even though other assets have a 20 MiB limit. A supported extension does not guarantee a valid decodable image or font.

For the @font-face example above, merge this fragment into the document root after adding the actual font file:

{
"resources": {
"stylesheets": ["theme.css"],
"assets": { "my-mono": "fonts/my-mono.woff2" }
},
"common": {
"editor": { "fontFamily": "\"My Mono\", monospace" },
"terminal": { "fontFamily": "\"My Mono\", monospace" }
}
}

Keep existing resources when merging; do not add a second resources key. Declaring a font asset checks its availability, while @font-face assigns the CSS family. Both parts serve a different purpose.

backgrounds uses separate image layers on the seven supported areas. Blur is measured in pixels and opacity ranges from 0 to 1. Start with a low opacity and check selection and text contrast on every affected surface. Use size: "cover" for a wallpaper or a fixed tile size with repeat: "repeat" for a pattern.

Manifest paths use forward slashes, for example images/grid.svg, without ./. CSS-relative URLs and icon-document paths have separate resolution rules and may contain ../ only while staying inside the package. Keep path letter case exact so packages remain portable.

Prefer tokens and supported layout settings for application changes. Selectors such as .workspace-info in the example are owned by that plugin; selectors for internal app markup may change. styles accepts declaration maps, not complete CSS text, nested rules or @media blocks. Put those in a stylesheet and use [data-appearance="light"] or [data-appearance="dark"] when targeting the effective Lomi appearance.

Avoid remote font imports, scripts and dependence on files outside the package. A successful outer CSS load is not proof that every nested resource loaded. Verify the final rendering and explicitly declare required assets.

Source: resource loading and CSS generation, native package limits.