Code Editor Examples
The examples below assume that you have imported the component and set up the theme.
Basic
The initial code comes from the slotted content, and the editor is a plain text area until a highlighter is assigned. expand grows it to fit the number of lines instead of scrolling inside a fixed height.
function greet(name) {
return "Hello, " + name;
} <sv-code-editor class="basic-editor" expand> <pre>function greet(name) { return "Hello, " + name;}</pre ></sv-code-editor>
<style> .basic-editor { max-inline-size: 32rem; margin-inline: auto; }</style>Syntax Highlighting
highlighter takes the whole code and returns the highlighted HTML for it, so any highlighting library works. The theme itself lives outside the shadow DOM, so code-stylesheet points at the <link> or <style> that holds it.
const message = "Hello World!";
for (const word of message.split(" ")) {
console.log(word);
} <!-- Light theme --><link class="hljs-light" href="highlight.js/styles/atom-one-light.min.css" rel="stylesheet" />
<!-- Dark theme --><link class="hljs-dark" href="highlight.js/styles/atom-one-dark.min.css" rel="stylesheet" />
<sv-code-editor class="highlighted-editor" expand> <pre>const message = "Hello World!";
for (const word of message.split(" ")) { console.log(word);}</pre ></sv-code-editor>
<script type="application/x-typescript" module> import hljs from "highlight.js/lib/core"; import typescript from "highlight.js/lib/languages/typescript";
hljs.registerLanguage("typescript", typescript);
const editor = document.querySelector<CodeEditor>(".highlighted-editor")!;
editor.highlighter = code => hljs.highlight(code, { language: "typescript" }).value;
// The docs site color scheme, a page of your own would pick one theme const preferred = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; const stored = document.documentElement.style.colorScheme; const colorScheme = stored === "dark" || stored === "light" ? stored : preferred;
editor.codeStylesheet = `.hljs-${colorScheme}`;
document.addEventListener("color-scheme-changed", event => { const theme = (event as CustomEvent<"light" | "dark">).detail as "light" | "dark"; editor.codeStylesheet = `.hljs-${theme}`; });</script>
<style> .highlighted-editor { max-inline-size: 32rem; margin-inline: auto; }</style>Line Numbers
linenumbers adds the gutter, and the line under the cursor is marked as active.
.card {
display: grid;
gap: 1rem;
padding: 1rem;
} <sv-code-editor class="numbered-editor" expand linenumbers> <pre>.card { display: grid; gap: 1rem; padding: 1rem;}</pre ></sv-code-editor>
<style> .numbered-editor { max-inline-size: 32rem; margin-inline: auto; }</style>Sizing
Without expand the editor fills the height you give it and scrolls, sideways as well when a line is too long. wrap breaks those long lines instead.
const rows = [1, 2, 3];
for (const row of rows) {
console.log(row);
} const label = "A single line that runs past the right edge of the editor and keeps going for a while";
const label = "A single line that runs past the right edge of the editor and keeps going for a while";
<div class="sizing-editors"> <div class="variant"> <sv-code-editor expand> <pre>const rows = [1, 2, 3];
for (const row of rows) { console.log(row);}</pre > </sv-code-editor> <span>expand</span> </div>
<div class="variant"> <sv-code-editor class="fixed-height"> <pre>const label = "A single line that runs past the right edge of the editor and keeps going for a while";</pre> </sv-code-editor> <span>fixed height, scrolls sideways</span> </div>
<div class="variant"> <sv-code-editor class="fixed-height" wrap> <pre>const label = "A single line that runs past the right edge of the editor and keeps going for a while";</pre> </sv-code-editor> <span>wrap</span> </div></div>
<style> .sizing-editors { display: grid; gap: 1.5em; justify-items: center; padding-block: 1em;
.variant { display: grid; gap: 0.4em; justify-items: center; inline-size: min(32rem, 100%);
> span { font-size: 0.8em; color: var(--sv-gray-3); } }
.fixed-height { block-size: 5rem; } }</style>Buttons
copy-button copies the whole code to the clipboard, and wrap-button toggles wrapping from inside the editor.
const response = await fetch("/api/session", { method: "DELETE", headers: { "content-type": "application/json" } });
if (!response.ok) {
throw new Error("Failed to end the session: " + response.status);
} <sv-code-editor class="buttons-editor" copy-button expand wrap-button> <pre>const response = await fetch("/api/session", { method: "DELETE", headers: { "content-type": "application/json" } });
if (!response.ok) { throw new Error("Failed to end the session: " + response.status);}</pre ></sv-code-editor>
<style> .buttons-editor { max-inline-size: 32rem; margin-inline: auto; }</style>Readonly
readonly blocks editing while keeping the code selectable, which is what a snippet display needs.
npm install @staticview/ui npm run dev
<sv-code-editor class="readonly-editor" copy-button expand readonly> <pre>npm install @staticview/uinpm run dev</pre ></sv-code-editor>
<style> .readonly-editor { max-inline-size: 32rem; margin-inline: auto; }</style>One Line
one-line makes the editor behave like a single line input: Enter does nothing and pasted newlines are stripped.
<div class="one-line-field"> <span class="one-line-label">Filter</span> <sv-code-editor class="one-line-input" expand one-line value="status == 'open' && assignee != null"></sv-code-editor></div>
<style> .one-line-field { display: grid; gap: 0.4em; max-inline-size: 32rem; margin-inline: auto;
.one-line-label { font-size: 0.85em; color: var(--sv-gray-3); }
.one-line-input { --sv-code-editor-font-sz: 0.95rem; --sv-code-editor-pad: 0.6rem; } }</style>Tab Size
tabsize is the number of spaces Tab inserts. Put the cursor at the start of a line and press Tab in both editors to compare.
if (ready) {
start();
}
if (ready) {
start();
} <div class="tabsize-editors"> <div class="variant"> <sv-code-editor expand tabsize="2"> <pre>if (ready) { start();}</pre > </sv-code-editor> <span>tabsize="2"</span> </div>
<div class="variant"> <sv-code-editor expand tabsize="8"> <pre>if (ready) { start();}</pre > </sv-code-editor> <span>tabsize="8"</span> </div></div>
<style> .tabsize-editors { display: grid; gap: 1.5em; justify-items: center; padding-block: 1em;
.variant { display: grid; gap: 0.4em; justify-items: center; inline-size: min(32rem, 100%);
> span { font-size: 0.8em; color: var(--sv-gray-3); } } }</style>Header and Footer
The header and footer slots sit above and below the code, inside the editor's border.
{
"port": 3000,
"open": true
} <sv-code-editor class="framed-editor" expand linenumbers> <span class="editor-header" slot="header">config.json</span>
<pre>{ "port": 3000, "open": true}</pre >
<span class="editor-footer" slot="footer">JSON</span></sv-code-editor>
<style> .framed-editor { max-inline-size: 32rem; margin-inline: auto;
.editor-header, .editor-footer { padding: 0.35em 0.8em; font-size: 0.8em; color: var(--sv-gray-2); background-color: var(--sv-gray-5); }
.editor-header { border-start-start-radius: var(--sv-bdr-rad-md); border-start-end-radius: var(--sv-bdr-rad-md); }
.editor-footer { text-align: end; border-end-start-radius: var(--sv-bdr-rad-md); border-end-end-radius: var(--sv-bdr-rad-md); } }</style>Styling
The font, line height, padding, and gutter width are custom properties on the host. Everything else is reached through parts, and :state(--focused) covers the focused editor.
export function total(items) {
return items.length;
} <sv-code-editor class="styled-editor" copy-button expand linenumbers> <pre>export function total(items) { return items.length;}</pre ></sv-code-editor>
<style> .styled-editor { --sv-code-editor-font-sz: 0.85rem; --sv-code-editor-ln-height: 1.9; --sv-code-editor-ln-num-width: 2.5rem; --sv-code-editor-pad: 1rem;
max-inline-size: 32rem; margin-inline: auto;
&::part(wrapper) { border-color: var(--sv-accent); border-radius: var(--sv-bdr-rad-lg); }
&:state(--focused)::part(wrapper) { box-shadow: 0 0 0 2px var(--sv-accent); }
&::part(copy-button) { border-radius: 999px; } }</style>