Skip to content

Theming

compose-highlight ships four Highlight.js themes out of the box. Those built-ins are precompiled into Kotlin constants at build time, so they do not parse CSS at runtime.

Built-in themes

Theme Style Helper
Tomorrow Light rememberTomorrowTheme()
Tomorrow Night Dark rememberTomorrowNightTheme()
Atom One Dark Dark rememberAtomOneDarkTheme()
Atom One Light Light rememberAtomOneLightTheme()

Built-in themes (tomorrow, tomorrowNight, atomOneDark, atomOneLight) are fast-path themes:

  • No Context required
  • No runtime CSS parsing
  • Color maps are read from precompiled color maps bundled with the library

Automatic light/dark switching

Pass a light and dark theme to HighlightThemeProvider. It picks the right one based on the system isSystemInDarkTheme() value:

import dev.hossain.highlight.ui.HighlightThemeProvider
import dev.hossain.highlight.ui.SyntaxHighlightedCode
import dev.hossain.highlight.ui.rememberTomorrowNightTheme
import dev.hossain.highlight.ui.rememberTomorrowTheme

HighlightThemeProvider(
    lightHighlightTheme = rememberTomorrowTheme(),
    darkHighlightTheme  = rememberTomorrowNightTheme(),
) {
    SyntaxHighlightedCode(code = snippet, language = "kotlin")
}

Force a specific theme

import dev.hossain.highlight.ui.HighlightThemeProvider
import dev.hossain.highlight.ui.rememberAtomOneLightTheme

HighlightThemeProvider(
    darkTheme           = false,  // always use the light theme
    lightHighlightTheme = rememberAtomOneLightTheme(),
    darkHighlightTheme  = rememberAtomOneLightTheme(),
) { ... }

Custom theme from a Highlight.js CSS file

  1. Download any .css file from highlightjs/highlight.js/src/styles (or create your own).
  2. Place it in src/main/assets/themes/.
  3. Load it with HighlightTheme.fromAsset():
import dev.hossain.highlight.engine.HighlightTheme
import dev.hossain.highlight.ui.HighlightThemeProvider

val theme = HighlightTheme.fromAsset(
    context   = LocalContext.current.applicationContext,
    assetPath = "themes/github-dark.css",
    name      = "github-dark",
)
HighlightThemeProvider(darkHighlightTheme = theme) { ... }

fromAsset() is lazy. CSS parsing happens on first use (theme.colorMap), not at factory-call time.

Note

HighlightTheme.fromAsset() normalizes the passed Context to applicationContext internally. Passing applicationContext at call sites is still recommended for clarity.

Custom theme from raw CSS

Useful when the CSS is fetched remotely or generated at runtime:

import dev.hossain.highlight.engine.HighlightTheme

val rawCss = // ... network fetch or string resource
val theme = HighlightTheme.fromCss(cssText = rawCss, name = "remote-theme")

Custom theme from a color map

Maximum flexibility - derive colors from Material 3 dynamic color, user palettes, or brand colors:

import dev.hossain.highlight.engine.HighlightTheme

val lightColors = MaterialTheme.colorScheme
val theme = HighlightTheme.fromColorMap(
    name             = "material-dynamic-light",
    colorMap         = mapOf(
        "hljs"          to SpanStyle(color = lightColors.onSurface, background = lightColors.surface),
        "hljs-keyword"  to SpanStyle(color = lightColors.primary, fontWeight = FontWeight.Bold),
        "hljs-string"   to SpanStyle(color = lightColors.tertiary),
        "hljs-comment"  to SpanStyle(color = lightColors.outline, fontStyle = FontStyle.Italic),
    ),
    backgroundColor  = lightColors.surface,
    defaultTextColor = lightColors.onSurface,
)

Providing a theme without HighlightThemeProvider

For one-off use - pass the theme directly:

import dev.hossain.highlight.ui.SyntaxHighlightedCode
import dev.hossain.highlight.ui.rememberTomorrowTheme

val theme = rememberTomorrowTheme()
SyntaxHighlightedCode(
    code     = snippet,
    language = "python",
    theme    = theme,
)

This creates a standalone HighlightEngine with its own WebView. Use HighlightThemeProvider when rendering multiple code blocks on the same screen.

Highlight both themes simultaneously

For zero-latency theme switching, tokenize once and apply both color maps. Use produceState to keep the work off the UI thread:

import dev.hossain.highlight.ui.rememberHighlightEngine

val engine = rememberHighlightEngine()
val fallback = remember(code) { AnnotatedString(code) }
val themedPair by produceState(fallback to fallback, code) {
    engine.highlightBothThemes(
        code       = code,
        language   = "kotlin",
        lightTheme = lightTheme,
        darkTheme  = darkTheme,
    ).onSuccess { value = it.light to it.dark }
}
val (lightAnnotated, darkAnnotated) = themedPair
// Switch between lightAnnotated and darkAnnotated instantly

See rememberHighlightedCodeBothThemes for the ready-made composable helper.