HTML Linked With Scoped CSS

Copy Markdown View Source
Mix.install([
  {:lumis, path: ".."}
])

formatter: :html_linked renders classes instead of inline styles. This is useful when your app has a strict Content Security Policy or when many code blocks share the same stylesheet.

code = """
defmodule Demo do
  def hello(name), do: "hello #{name}"
end
"""

html =
  Lumis.highlight!(code,
    formatter: {:html_linked, language: "elixir"}
  )

The generated HTML contains semantic token classes:

html

Build CSS for the light theme:

light_css = Lumis.Theme.build_css!("github_light")

By default, Lumis emits the same selector shape as the bundled CSS files:

.lumis {
  color: #1f2328;
  background-color: #ffffff;
}
.l-keyword {
  color: #cf222e;
}

Use :container_selector when your code block uses a different wrapper class. This changes only the container rule selector:

Lumis.Theme.build_css!("github_light",
  container_selector: ".code-block"
)

Output shape:

.code-block {
  color: #1f2328;
  background-color: #ffffff;
}
.l-keyword {
  color: #cf222e;
}

Use :scope to put every selector under a parent selector. This is commonly used for manual dark mode selectors:

dark_css =
  Lumis.Theme.build_css!("github_dark",
    scope: ~s(html[data-theme="dark"]),
    container_selector: ".lumis",
    container_style: [
      {"background-color", "var(--code-background)"},
      {"border-radius", "0.375rem"},
      {"padding", "1rem"},
      {"overflow-x", "auto"}
    ]
  )

Output shape:

html[data-theme="dark"] .lumis {
  color: #e6edf3;
  background-color: var(--code-background);
  border-radius: 0.375rem;
  padding: 1rem;
  overflow-x: auto;
}
html[data-theme="dark"] .l-keyword {
  color: #ff7b72;
}

You can combine light and dark CSS and embed or serve the result however your application needs:

css = light_css <> "\n\n" <> dark_css