Code Highlighter
<quiet-code-highlighter>
Renders syntax-highlighted code using small, battle-tested grammars that load on demand. Reach for it whenever you want to show highlighted code blocks on the page.
Highlighting is powered by
, a mature library with grammars for virtually every language you'll encounter. The component works like a
<pre> tag: put code inside it, tell it the language, and it renders an accessible,
themeable code block. HTML, CSS, JavaScript, and TypeScript are available instantly, while
other
load on demand from the library's assets, so you never fetch grammars you don't use.
<quiet-code-highlighter language="html">
<button type="button" class="primary" disabled>
<quiet-icon name="rocket"></quiet-icon>
Launch
</button>
</quiet-code-highlighter>
Like a <pre> tag, the code renders exactly as written. Content is automatically
dedented, so feel free to indent it naturally in your markup. If you need to
preserve indentation, add the without-dedent attribute.
Examples Jump to heading
Setting the language Jump to heading
Set the language attribute to the language the code is written in. Common aliases work too,
e.g. js, ts, html, py, and php. If you
don't set a language, the code renders as plain text. See
thisÂ
for available languages.
<quiet-code-highlighter language="php">
<?php
function greet(string $name = 'world'): bool {
echo "Hello, {$name}!";
return true;
}
</quiet-code-highlighter>
Escaping markup Jump to heading
Because the browser parses your HTML before the component sees it, encode your code the way you would for
any HTML: replace < with <, & with
&, and > with >. This is exactly what a server-side
escaping function does, and it renders correctly no matter what the code contains. When encoding by hand
gets tedious, set the code with the code property
instead, which renders verbatim.
You can often skip encoding, however. A < only causes trouble when it's immediately followed
by something tag-like, as in generics (Map<string, User>) or literal markup, and a
& only when it forms a character reference (&,
©). A < used as a comparison (x < 100) or a standalone
& (a && b) passes through untouched. Relying on this keeps your code
readable, but it's on you to catch the cases that need encoding.
<quiet-code-highlighter language="ts">
const users: Map<string, User> = new Map();
// Comparisons are fine without escaping
if (users.size < 100) {
users.set('cat', { name: 'Whiskers' });
}
</quiet-code-highlighter>
If the component finds HTML elements in its content, it logs a helpful warning to the console because it
usually means a < slipped through unescaped. When escaping gets tedious, set the code with
the code property instead.
HTML formatters and minifiers know to leave <pre> alone, but they don't know about
custom elements, so they may collapse the whitespace inside this one. If your toolchain reformats HTML,
exclude these elements from processing, e.g. <!-- prettier-ignore -->, or use the
code property.
Setting code programmatically Jump to heading
Set the code property to highlight code from JavaScript. When set, it always wins over slotted
content and renders verbatim, without dedenting. This is the escape hatch for dynamic code and for
frameworks where escaping is impractical.
<quiet-code-highlighter id="code-highlighter__dynamic" language="css"></quiet-code-highlighter>
<quiet-text-area
id="code-highlighter__dynamic-input"
label="CSS"
value=".cat { color: gray; cursor: pointer; }"
rows="4"
style="margin-block-start: 1rem;"
></quiet-text-area>
<script type="module">
import { allDefined } from '/dist/quiet.js';
await allDefined();
const highlighter = document.getElementById('code-highlighter__dynamic');
const input = document.getElementById('code-highlighter__dynamic-input');
highlighter.code = input.value;
input.addEventListener('quiet-input', () => {
highlighter.code = input.value;
});
</script>
Loading additional languages Jump to heading
Grammars for
hundreds of
beyond HTML, CSS, JavaScript, and TypeScript load automatically the first time they're used. They're fetched
from the library's assets/grammars folder
using the library path, the same way icons load. While a
grammar loads, the code shows as plain text and the loading custom state is applied. Colors
appear as soon as it arrives.
<quiet-code-highlighter language="rust">
/// A cat that collects treats.
struct Cat {
name: String,
treat_count: u32,
}
impl Cat {
fn new(name: &str) -> Self {
Self { name: name.into(), treat_count: 0 }
}
fn nibble(&mut self) {
self.treat_count += 1;
}
}
</quiet-code-highlighter>
If a grammar can't be found, the component dispatches the quiet-load-error event, applies the
error custom state, and renders the code as plain text.
Registering custom grammars Jump to heading
Use the registerCodeGrammar() function to add languages the library doesn't ship, using the
same
language definition
highlight.js uses. Registered grammars take effect immediately, even for code highlighters already on the
page.
<quiet-code-highlighter id="code-highlighter__custom" language="treatlang">
order tuna 12
order salmon 3
# cats prefer chicken
order chicken 999
</quiet-code-highlighter>
<script type="module">
import { registerCodeGrammar } from '/dist/quiet.js';
registerCodeGrammar('treatlang', () => ({
name: 'Treatlang',
contains: [
{ scope: 'comment', begin: /#/, end: /$/ },
{ scope: 'keyword', begin: /\border\b/ },
{ scope: 'number', begin: /\b\d+\b/ }
]
}));
</script>
Changing the color scheme Jump to heading
Code blocks follow the surrounding theme by default, rendering light until they're inside a
quiet-dark container. Set the
color-scheme attribute to light or dark to force one, e.g. for a dark
code block on an otherwise light page.
<quiet-code-highlighter language="css" color-scheme="light">
/* Always light */
.cat {
color: gray;
}
</quiet-code-highlighter>
<quiet-code-highlighter language="css" color-scheme="dark">
/* Always dark */
.cat {
color: gray;
}
</quiet-code-highlighter>
Preserving indentation Jump to heading
Slotted code is dedented automatically, so indenting it to match your markup doesn't leak into the output.
Leading and trailing blank lines are removed, too. Add the without-dedent attribute to opt out,
giving you <pre> semantics where a single newline after the opening tag is ignored and
everything else is preserved. As with <pre>, place the closing tag right after the last
character to avoid rendering trailing whitespace.
<quiet-code-highlighter language="plaintext" without-dedent>
This line keeps its four leading spaces.
This one keeps six.</quiet-code-highlighter>
Wrapping long lines Jump to heading
By default, long lines cause the code block to scroll horizontally. Add the wrap attribute to
soft-wrap them instead.
<quiet-code-highlighter language="bash" wrap>
curl -X POST https://api.example.com/v1/treats/orders -H "Content-Type: application/json" -d '{"flavor": "tuna", "quantity": 3, "notes": "For the cats in the server room"}'
</quiet-code-highlighter>
Changing the label Jump to heading
The code block is exposed to assistive devices as a region labeled "Code" in
the configured language. Use the label attribute to provide a
more descriptive one.
<quiet-code-highlighter language="json" label="Example API response">
{
"name": "Whiskers",
"occupation": "Nap supervisor",
"treats": ["tuna", "salmon"]
}
</quiet-code-highlighter>
Removing the copy button Jump to heading
A copy button appears in the top corner when you hover over the code block. It always copies the code being
displayed, whether it comes from the slot or the code property. Add the
without-copy attribute to remove it.
<quiet-code-highlighter language="sql" without-copy>
SELECT name, favorite_treat FROM cats WHERE mood = 'sleepy' ORDER BY naps DESC;
</quiet-code-highlighter>
Styling code blocks Jump to heading
In dark mode, syntax colors match the palette the Quiet docs use for code. Use the
custom properties to change token colors and the
::part(pre) part to restyle the container. Provide colors with
to theme both modes at once, or plain colors to theme them uniformly.
For styles beyond color, every token exposes a token-* CSS part named
after the custom property that colors it, e.g. ::part(token-comment) for italic comments. All
tokens also carry the token part, so you can target every token at once.
This example recreates in dark mode and its official light counterpart, Alucard, in light mode. It also gives the container a thicker border and a different font, and uses the token parts to apply Dracula's signature italics to attributes and parameters.
<quiet-code-highlighter language="js" class="code-highlighter__dracula">
// It's just a phase, mom
const cat = {
name: 'Count Whiskula',
age: 3,
findTreat() {
return ['tuna', 'salmon'].at(Math.floor(Math.random() * 2));
}
};
</quiet-code-highlighter>
<style>
.code-highlighter__dracula {
--attribute-color: light-dark(#14710a, #50fa7b);
--comment-color: light-dark(#635d97, #6272a4);
--deletion-color: light-dark(#cb3a2a, #ff5555);
--function-color: light-dark(#14710a, #50fa7b);
--keyword-color: light-dark(#a3144d, #ff79c6);
--number-color: light-dark(#644ac9, #bd93f9);
--punctuation-color: light-dark(#1f1f1f, #f8f8f2);
--string-color: light-dark(#846e15, #f1fa8c);
--tag-color: light-dark(#a3144d, #ff79c6);
--variable-color: light-dark(#a34d14, #ffb86c);
&::part(pre) {
border: solid 2px light-dark(#644ac966, #bd93f966);
border-radius: var(--quiet-border-radius-md);
background-color: light-dark(#fffbeb, #282a36);
color: light-dark(#1f1f1f, #f8f8f2);
font-family: 'Code New Roman', 'Cascadia Code', 'JetBrains Mono', monospace;
}
&::part(copy) {
margin-inline-end: 2px;
margin-block-start: 2px;
}
&::part(token-attribute),
&::part(token-variable) {
font-style: italic;
}
&::part(copy__copy-button__button) {
background-color: light-dark(#fffbeb, #282a36);
color: light-dark(#1f1f1f, #f8f8f2);
}
}
</style>
Keyboard support Jump to heading
When code overflows its container, tab to the code block to focus it, then use the keys below.
| Key | Action |
|---|---|
| Arrow Keys | Scrolls the code in the pressed direction |
| Home | Scrolls to the beginning of the code |
| End | Scrolls to the end of the code |
API Jump to heading
Importing Jump to heading
The autoloader is the recommended way to import components but, if you prefer to do it manually, the following code snippets will be helpful.
To manually import <quiet-code-highlighter> from the CDN, use the following code.
import 'https://cdn.quietui.org/v5.3.1/components/code-highlighter/code-highlighter.js';
To manually import <quiet-code-highlighter> from a self-hosted distribution, use the
following code. Remember to replace /path/to/quiet with the appropriate local path.
import '/path/to/quiet/components/code-highlighter/code-highlighter.js';
Slots Jump to heading
Code Highlighter supports the following slots. Learn more about using slots
| Name | Description |
|---|---|
| (default) |
The code to highlight. Like the content of a <pre> tag, it renders exactly as
written, so encode < as < and & as
& when the code contains markup or character references. Slotted code is
dedented automatically unless without-dedent is set.
|
Properties Jump to heading
Code Highlighter has the following properties that can be set with corresponding attributes. In many cases, the attribute's name is the same as the property's name. If an attribute is different, it will be displayed after the property. Learn more about attributes and properties
| Property | Description | Reflects | Type | Default |
|---|---|---|---|---|
code
|
The code to highlight. When set, it takes precedence over slotted content and renders verbatim, without dedenting. Use when the code is dynamic or when escaping slotted content is impractical. |
|
string
|
|
language
|
The language the code is written in. Any grammar name or alias that ships with the library works,
e.g. html, css, js, and ts are available
immediately and others such as python or rust load on demand.
|
|
string
|
'plaintext'
|
label
|
An accessible label for the code region. If omitted, a localized fallback is used instead. |
|
string
|
''
|
colorScheme
color-scheme
|
The color scheme of the code block. When auto, the scheme is inherited from the
surrounding theme, i.e. light by default and dark inside a quiet-dark container. Use
light or dark to force one.
|
|
'light' |
'dark' |
'auto'
|
'auto'
|
withoutCopy
without-copy
|
Removes the copy button. |
|
boolean
|
false
|
withoutDedent
without-dedent
|
Preserves slotted code exactly as written instead of dedenting it, matching the behavior of a
<pre> tag. A single newline immediately after the opening tag is ignored and all
other whitespace is kept, including trailing whitespace. Has no effect on the
code property, which always renders verbatim.
|
|
boolean
|
false
|
wrap
|
Wraps long lines instead of showing a horizontal scrollbar. |
|
boolean
|
false
|
Events Jump to heading
Code Highlighter dispatches the following custom events. You can listen to them the same way was native events. Learn more about custom events
| Name | Description |
|---|---|
quiet-loaded |
Emitted when a grammar has been loaded and the code has been highlighted with it. |
quiet-load-error |
Emitted when a grammar fails to load. The code is rendered as plain text in this case. |
CSS custom properties Jump to heading
Code Highlighter supports the following CSS custom properties. You can style them like any other CSS property. Learn more about CSS custom properties
| Name | Description | Default |
|---|---|---|
--attribute-color |
The color of attribute names and selectors. |
light-dark(#3a7a2a, #9bc789)
|
--comment-color |
The color of comments. |
light-dark(#7c7d87, #a4a6b0)
|
--deletion-color |
The color of deletions and symbols. |
light-dark(#c13b6f, #eea3bb)
|
--function-color |
The color of function, class, and section titles. |
light-dark(#b25d09, #e4b073)
|
--keyword-color |
The color of keywords, literals, and built-ins. |
light-dark(#315bc4, #98bafe)
|
--number-color |
The color of numbers and regular expressions. |
light-dark(#b25d09, #e4b073)
|
--punctuation-color |
The color of operators and punctuation. |
light-dark(#7c7d87, #a4a6b0)
|
--string-color |
The color of strings. |
light-dark(#3a7a2a, #9bc789)
|
--tag-color |
The color of tags and tag names. |
light-dark(#315bc4, #98bafe)
|
--variable-color |
The color of variables, properties, and parameters. |
light-dark(#b25d09, #e4b073)
|
CSS parts Jump to heading
Code Highlighter exposes internal elements that can be styled with CSS using the selectors shown below. Learn more about CSS parts
| Name | Description | CSS selector |
|---|---|---|
pre |
The <pre> element that acts as the scroll container.
|
::part(pre)
|
code |
The <code> element that contains the highlighted markup.
|
::part(code)
|
token |
Any highlighted token. Every token carries this part in addition to its token-* part,
so use it to style all tokens at once.
|
::part(token)
|
token-attribute |
Attribute names and selectors. |
::part(token-attribute)
|
token-comment |
Comments. |
::part(token-comment)
|
token-deletion |
Deletions and symbols. |
::part(token-deletion)
|
token-function |
Function, class, and section titles. |
::part(token-function)
|
token-keyword |
Keywords, literals, and built-ins. |
::part(token-keyword)
|
token-number |
Numbers and regular expressions. |
::part(token-number)
|
token-punctuation |
Operators and punctuation. |
::part(token-punctuation)
|
token-string |
Strings. |
::part(token-string)
|
token-tag |
Tags and tag names. |
::part(token-tag)
|
token-variable |
Variables, properties, and parameters. |
::part(token-variable)
|
copy |
The copy button, a <quiet-copy> element. |
::part(copy)
|
copy__copy-button |
The copy button's copy-button part. |
::part(copy__copy-button)
|
copy__copy-button__button |
The copy button's copy-button__button part. |
::part(copy__copy-button__button)
|
copy__copy-icon |
The copy button's copy-icon part. |
::part(copy__copy-icon)
|
copy__feedback |
The copy button's feedback part. |
::part(copy__feedback)
|
Custom States Jump to heading
Code Highlighter has the following custom states. You can target them with CSS using the selectors shown below. Learn more about custom states
| Name | Description | CSS selector |
|---|---|---|
loading |
Applied while a grammar is being fetched. |
:state(loading)
|
error |
Applied when the grammar failed to load or resolve. |
:state(error)
|
Dependencies Jump to heading
Code Highlighter automatically imports the following elements. Sub-dependencies are also included in this list.