# Line Highlighting (/recipes/line-highlighting)

Line highlighting lets you emphasize key lines without changing the rest of the block.

## Common patterns

- highlight a few exact lines
- highlight a range such as `3..5`
- use theme styling or your own CSS class / inline style

## Examples

### JavaScript / TypeScript

```js
import {highlight} from '@lumis-sh/lumis';
import {htmlInline} from '@lumis-sh/lumis/formatters';
import javascript from '@lumis-sh/wasm-javascript';
import frappe from '@lumis-sh/themes/catppuccin_frappe';

const html = await highlight(
  'line 1\nline 2\nline 3\nline 4',
  htmlInline({
    language: javascript,
    theme: frappe,
    highlightLines: {lines: [1, [3, 4]], style: 'theme'},
  })
);
```

For linked HTML, use `htmlLinked({ highlightLines: { lines: [1, [3, 4]], class: 'active-line' } })`.

### Rust

```rust
use lumis::formatters::html_inline::{
    HighlightLines, HighlightLinesStyle,
};
use lumis::{
    highlight, languages::Language, themes,
    HtmlInlineBuilder,
};

let highlight_lines = HighlightLines {
    lines: vec![1..=1, 3..=4],
    style: Some(HighlightLinesStyle::Theme),
    class: None,
};

let formatter = HtmlInlineBuilder::new()
    .language(Language::PlainText)
    .theme(Some(themes::get("catppuccin_frappe").unwrap()))
    .highlight_lines(Some(highlight_lines))
    .build()
    .unwrap();

let html =
    highlight("line 1\nline 2\nline 3\nline 4", formatter);
```

### Elixir

```elixir
Lumis.highlight!(
  "line 1\nline 2\nline 3\nline 4",
  formatter: {:html_inline,
    language: "plaintext",
    theme: "catppuccin_frappe",
    highlight_lines: %{
      lines: [1, 3..4],
      style: :theme
    }
  }
)
```

For linked HTML, switch to `:html_linked` and set `class: "active-line"`.

### Java

Line highlighting is not currently exposed by `lumis4j`.

### CLI

```bash
lumis highlight main.rs \
  --formatter html-inline \
  --highlight-lines 1,3-5,10
```


## Custom style example

### JavaScript / TypeScript

```js
import {highlight} from '@lumis-sh/lumis';
import {htmlInline} from '@lumis-sh/lumis/formatters';
import javascript from '@lumis-sh/wasm-javascript';
import frappe from '@lumis-sh/themes/catppuccin_frappe';

const html = await highlight(
  'line 1\nline 2\nline 3\nline 4',
  htmlInline({
    language: javascript,
    theme: frappe,
    highlightLines: {
      lines: [2, [4, 4]],
      style:
        'background-color: rgba(255, 221, 87, 0.18); ' +
        'border-left: 3px solid #ffd54f;',
    },
  })
);
```

### Rust

```rust
use lumis::formatters::html_inline::{
    HighlightLines, HighlightLinesStyle,
};
use lumis::{
    highlight, languages::Language, themes,
    HtmlInlineBuilder,
};

let style = concat!(
    "background-color: rgba(255, 221, 87, 0.18); ",
    "border-left: 3px solid #ffd54f;"
);

let highlight_lines = HighlightLines {
    lines: vec![2..=2, 4..=4],
    style: Some(HighlightLinesStyle::Style(
        style.to_string(),
    )),
    class: None,
};

let formatter = HtmlInlineBuilder::new()
    .language(Language::PlainText)
    .theme(Some(themes::get("catppuccin_frappe").unwrap()))
    .highlight_lines(Some(highlight_lines))
    .build()
    .unwrap();

let html =
    highlight("line 1\nline 2\nline 3\nline 4", formatter);
```

### Elixir

```elixir
Lumis.highlight!(
  "line 1\nline 2\nline 3\nline 4",
  formatter:
    {:html_inline,
     language: "plaintext",
     theme: "catppuccin_frappe",
     highlight_lines: %{
       lines: [2, 4],
       style:
         "background-color: rgba(255, 221, 87, 0.18); " <>
           "border-left: 3px solid #ffd54f;"
     }}
)
```


## Class without inline colors

Leaving `style` out uses the theme's `highlighted` style. To color highlighted
lines with your own class instead, from a stylesheet or a Tailwind utility, turn
the inline colors off. The line keeps its inline layout, so the class only has to
set a background:

### JavaScript / TypeScript

```js
htmlInline({
  language: javascript,
  theme: frappe,
  highlightLines: {
    lines: [2],
    style: null,
    class: 'bg-yellow-500',
  },
})
```

`null` opts out; leaving `style` off entirely still uses the theme.

### Rust

```rust
HighlightLines {
    lines: vec![2..=2],
    style: None,
    class: Some("bg-yellow-500".to_string()),
}
```

### Elixir

```elixir
{:html_inline,
 highlight_lines: %{
   lines: [2],
   style: nil,
   class: "bg-yellow-500"
 }}
```

### CLI

```bash
lumis highlight -f html-inline -t catppuccin_frappe -H 2 \
  --highlight-lines-style none \
  --highlight-lines-class bg-yellow-500 \
  app.js
```


## Full-width backgrounds

A highlighted line spans the whole block, including the part you only reach by
scrolling sideways. For HTML Linked, the theme stylesheet does this with a few
rules in `@layer lumis`. HTML Inline and HTML Multi-Themes write the same
declarations inline when lines are highlighted. The
[Line layout](/themes/css-files#line-layout) section lists the rules.

If you style lines yourself, keep `.l-line` inline or inline-block. With
`display: block`, the newline between two lines becomes an extra blank row. Add
`box-sizing: border-box` when your line rule adds padding or a border. For
Tailwind, see [Tailwind CSS](/recipes/tailwind-css).

## What changes between formatters

In Rust, the npm package, Elixir and the CLI, every formatter takes the same
`lines`. What differs is how it marks them, because each output format has a
different thing to mark a line with. `lumis4j` does not expose line
highlighting.

| Formatter | Highlight style |
| --- | --- |
| HTML Inline | theme style, custom inline CSS, or class only |
| HTML Linked | CSS class names |
| HTML Multi-Themes | theme style, custom inline CSS, or class only |
| Terminal | a background colour, from `background` or the theme's `highlighted` style |
| BBCode Scoped | a `[highlighted]` tag around the line |

### Terminal

A terminal has no class to hang a stylesheet off, so a highlighted line is
painted with a background. Only the background is taken from the theme: a
foreground would overwrite the colour every token on the line was already given.
With neither a `background` nor a theme that styles `highlighted`, nothing marks
the line.

```rust
use lumis::formatters::terminal::HighlightLines;

let highlight_lines = HighlightLines {
    lines: vec![1..=1, 3..=4],
    // Omit it to take the theme's `highlighted` background.
    background: Some("#3a3a3a".to_string()),
};
```

```js
terminal({
  language: javascript,
  theme: frappe,
  highlightLines: {lines: [1, [3, 4]]},
});
```

```elixir
{:terminal,
 theme: "catppuccin_frappe",
 highlight_lines: %{lines: [1, 3..4]}}
```

```sh
lumis highlight -f terminal \
  -t catppuccin_frappe -H 1,3-4 main.rs
lumis highlight -f terminal -H 1,3-4 \
  --highlight-lines-background '#3a3a3a' \
  main.rs
```

### BBCode Scoped

A highlighted line is wrapped in `[highlighted]...[/highlighted]`, newline
included. Unlike HTML, BBCode keeps its source terminators. The tag is derived from
the `highlighted` theme scope exactly as every other tag this formatter emits is
derived from a scope, so there is nothing to configure; the consumer defines what
`[highlighted]` looks like, as it already must for `[keyword-rust]`.

```sh
lumis highlight -f bbcode-scoped -H 1,3-4 main.rs
```

Without `highlight_lines`, Terminal and BBCode emit tokens without line
wrappers. Line highlighting closes and reopens scopes at line boundaries so it
can mark individual lines.