j3w1 / Code editorVue · HTML · React · Astro

J3W1 / DESIGN SYSTEM

Code editor

The editor surface, gutter, caret, current line, selection, bracket match and monochrome syntax roles that every code-editing or code-viewing host maps onto.

stable · LIGHT DOM · 1.1.0

Light DOM retains static content, native semantics, label relationships and host composition without duplicating controls.

Live variants

default

// Compose the window title of the focused container.
import { Container } from "./tree.js";
const MAX_LENGTH = 48;
export function title(container) {
  const name = container.name ?? "untitled";
  let label = `${name} (${container.children.length})`;
  if (label.length > MAX_LENGTH) {
    label = label.slice(0, MAX_LENGTH) + "…";
  }
  return escape(label); /* escape() is deprecated */
}
const broken = 0x1G;

<j3w1-code-editor id="example-code-editor-default"><div class="code-editor" role="textbox" aria-multiline="true" aria-readonly="true" aria-label="title.js, read-only" tabindex="0">
  <pre class="code-editor-pre"><span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">1</span><span class="code-editor-text"><span class="code-editor-cm">// Compose the window title of the focused container.</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">2</span><span class="code-editor-text"><span class="code-editor-kw">import</span> <span class="code-editor-punct">{</span> <span class="code-editor-type">Container</span> <span class="code-editor-punct">}</span> <span class="code-editor-kw">from</span> <span class="code-editor-str">"./tree.js"</span><span class="code-editor-punct">;</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">3</span><span class="code-editor-text"><span class="code-editor-kw">const</span> <span class="code-editor-const">MAX_LENGTH</span> <span class="code-editor-op">=</span> <span class="code-editor-num">48</span><span class="code-editor-punct">;</span><span class="code-editor-ws" aria-hidden="true">··</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">4</span><span class="code-editor-text"><span class="code-editor-kw">export</span> <span class="code-editor-kw">function</span> <span class="code-editor-fn">title</span><span class="code-editor-punct code-editor-bracket">(</span><span class="code-editor-var">container</span><span class="code-editor-punct code-editor-bracket">)</span> <span class="code-editor-punct">{</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">5</span><span class="code-editor-text"><span class="code-editor-indent">  </span><span class="code-editor-kw">const</span> <span class="code-editor-var">name</span> <span class="code-editor-op">=</span> <span class="code-editor-var">container</span><span class="code-editor-punct">.</span><span class="code-editor-prop">name</span> <span class="code-editor-op">??</span> <span class="code-editor-str">"untitled<span class="code-editor-esc">…</span>"</span><span class="code-editor-punct">;</span></span></span>
<span class="code-editor-line code-editor-line-current"><span class="code-editor-ln" aria-hidden="true">6</span><span class="code-editor-text"><span class="code-editor-indent">  </span><span class="code-editor-kw">let</span> <span class="code-editor-var">label</span> <span class="code-editor-op">=</span> <span class="code-editor-str">`${</span><span class="code-editor-var">name</span><span class="code-editor-str">} (${</span><span class="code-editor-var">container</span><span class="code-editor-punct">.</span><span class="code-editor-prop">children</span><span class="code-editor-punct">.</span><span class="code-editor-prop">length</span><span class="code-editor-str">})`</span><span class="code-editor-punct">;</span><span class="code-editor-caret" aria-hidden="true"></span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">7</span><span class="code-editor-text"><span class="code-editor-indent">  </span><span class="code-editor-kw">if</span> <span class="code-editor-punct">(</span><span class="code-editor-sel"><span class="code-editor-var">label</span><span class="code-editor-punct">.</span><span class="code-editor-prop">length</span> <span class="code-editor-op">&gt;</span> <span class="code-editor-const">MAX_LENGTH</span><span class="code-editor-punct">)</span> <span class="code-editor-punct">{</span></span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">8</span><span class="code-editor-text"><span class="code-editor-sel"><span class="code-editor-indent">  </span><span class="code-editor-indent">  </span><span class="code-editor-var">label</span> <span class="code-editor-op">=</span> <span class="code-editor-var">label</span><span class="code-editor-punct">.</span><span class="code-editor-fn">slice</span><span class="code-editor-punct">(</span><span class="code-editor-num">0</span><span class="code-editor-punct">,</span> <span class="code-editor-const">MAX_LENGTH</span><span class="code-editor-punct">)</span></span> <span class="code-editor-op">+</span> <span class="code-editor-str">"…"</span><span class="code-editor-punct">;</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">9</span><span class="code-editor-text"><span class="code-editor-indent">  </span><span class="code-editor-punct">}</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">10</span><span class="code-editor-text"><span class="code-editor-indent">  </span><span class="code-editor-kw">return</span> <span class="code-editor-fn code-editor-deprecated">escape</span><span class="code-editor-punct">(</span><span class="code-editor-var">label</span><span class="code-editor-punct">);</span> <span class="code-editor-cm">/* escape() is deprecated */</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">11</span><span class="code-editor-text"><span class="code-editor-punct">}</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">12</span><span class="code-editor-text"><span class="code-editor-kw">const</span> <span class="code-editor-var">broken</span> <span class="code-editor-op">=</span> <span class="code-editor-num">0x1</span><span class="code-editor-invalid">G</span><span class="code-editor-punct">;</span></span></span></pre>
</div></j3w1-code-editor>

markup

<!doctype html>
<!-- window frame specimen -->
<html lang="en">
  <head>
    <title>j3w1 &mdash; theme</title>
  </head>
  <body class="frame" data-density="compact">
    <h1>Window frame</h1>
    <marquee>obsolete</marquee>
    <p>Focus is a ring; selection is a fill.</p>
  </body>
</html><

<j3w1-code-editor id="example-code-editor-markup"><div class="code-editor" role="textbox" aria-multiline="true" aria-readonly="true" aria-label="frame.html, read-only" tabindex="0">
  <pre class="code-editor-pre"><span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">1</span><span class="code-editor-text"><span class="code-editor-punct">&lt;!</span><span class="code-editor-kw">doctype</span> <span class="code-editor-attr">html</span><span class="code-editor-punct">&gt;</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">2</span><span class="code-editor-text"><span class="code-editor-cm">&lt;!-- window frame specimen --&gt;</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">3</span><span class="code-editor-text"><span class="code-editor-punct">&lt;</span><span class="code-editor-tag">html</span> <span class="code-editor-attr">lang</span><span class="code-editor-op">=</span><span class="code-editor-str">"en"</span><span class="code-editor-punct">&gt;</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">4</span><span class="code-editor-text"><span class="code-editor-indent">  </span><span class="code-editor-punct">&lt;</span><span class="code-editor-tag">head</span><span class="code-editor-punct">&gt;</span></span></span>
<span class="code-editor-line code-editor-line-current"><span class="code-editor-ln" aria-hidden="true">5</span><span class="code-editor-text"><span class="code-editor-indent">  </span><span class="code-editor-indent">  </span><span class="code-editor-punct">&lt;</span><span class="code-editor-tag">title</span><span class="code-editor-punct">&gt;</span>j3w1 <span class="code-editor-esc">&amp;mdash;</span> theme<span class="code-editor-punct">&lt;/</span><span class="code-editor-tag">title</span><span class="code-editor-punct">&gt;</span><span class="code-editor-caret" aria-hidden="true"></span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">6</span><span class="code-editor-text"><span class="code-editor-indent">  </span><span class="code-editor-punct">&lt;/</span><span class="code-editor-tag">head</span><span class="code-editor-punct">&gt;</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">7</span><span class="code-editor-text"><span class="code-editor-indent">  </span><span class="code-editor-punct">&lt;</span><span class="code-editor-tag">body</span> <span class="code-editor-attr">class</span><span class="code-editor-op">=</span><span class="code-editor-str">"frame"</span> <span class="code-editor-attr">data-density</span><span class="code-editor-op">=</span><span class="code-editor-str">"compact"</span><span class="code-editor-punct">&gt;</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">8</span><span class="code-editor-text"><span class="code-editor-indent">  </span><span class="code-editor-indent">  </span><span class="code-editor-punct code-editor-bracket">&lt;</span><span class="code-editor-tag">h1</span><span class="code-editor-punct code-editor-bracket">&gt;</span><span class="code-editor-heading">Window frame</span><span class="code-editor-punct">&lt;/</span><span class="code-editor-tag">h1</span><span class="code-editor-punct">&gt;</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">9</span><span class="code-editor-text"><span class="code-editor-indent">  </span><span class="code-editor-indent">  </span><span class="code-editor-punct">&lt;</span><span class="code-editor-tag code-editor-deprecated">marquee</span><span class="code-editor-punct">&gt;</span>obsolete<span class="code-editor-punct">&lt;/</span><span class="code-editor-tag code-editor-deprecated">marquee</span><span class="code-editor-punct">&gt;</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">10</span><span class="code-editor-text"><span class="code-editor-indent">  </span><span class="code-editor-indent">  </span><span class="code-editor-punct">&lt;</span><span class="code-editor-tag">p</span><span class="code-editor-punct">&gt;</span><span class="code-editor-sel">Focus is a ring; selection is a fill.</span><span class="code-editor-punct">&lt;/</span><span class="code-editor-tag">p</span><span class="code-editor-punct">&gt;</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">11</span><span class="code-editor-text"><span class="code-editor-indent">  </span><span class="code-editor-sel"><span class="code-editor-punct">&lt;/</span><span class="code-editor-tag">body</span><span class="code-editor-punct">&gt;</span></span><span class="code-editor-ws" aria-hidden="true">·</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">12</span><span class="code-editor-text"><span class="code-editor-punct">&lt;/</span><span class="code-editor-tag">html</span><span class="code-editor-punct">&gt;</span><span class="code-editor-invalid">&lt;</span></span></span></pre>
</div></j3w1-code-editor>

folded

export function title(container) {
  const name = container.name ?? "untitled";
  if (label.length > MAX_LENGTH) {  }
  return escape(label); /* escape() is deprecated */
}

<j3w1-code-editor id="example-code-editor-folded"><div class="code-editor" role="textbox" aria-multiline="true" aria-readonly="true" aria-label="title.js with a folded region, read-only" tabindex="0">
  <pre class="code-editor-pre"><span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">4</span><span class="code-editor-text"><span class="code-editor-kw">export</span> <span class="code-editor-kw">function</span> <span class="code-editor-fn">title</span><span class="code-editor-punct code-editor-bracket">(</span><span class="code-editor-var">container</span><span class="code-editor-punct code-editor-bracket">)</span> <span class="code-editor-punct">{</span></span></span>
<span class="code-editor-line code-editor-line-current"><span class="code-editor-ln" aria-hidden="true">5</span><span class="code-editor-text"><span class="code-editor-indent">  </span><span class="code-editor-kw">const</span> <span class="code-editor-var">name</span> <span class="code-editor-op">=</span> <span class="code-editor-var">container</span><span class="code-editor-punct">.</span><span class="code-editor-prop">name</span> <span class="code-editor-op">??</span> <span class="code-editor-str">"untitled"</span><span class="code-editor-punct">;</span><span class="code-editor-caret" aria-hidden="true"></span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true"><span class="code-editor-fold-gutter">›</span>7</span><span class="code-editor-text"><span class="code-editor-indent">  </span><span class="code-editor-kw">if</span> <span class="code-editor-punct">(</span><span class="code-editor-sel"><span class="code-editor-var">label</span><span class="code-editor-punct">.</span><span class="code-editor-prop">length</span> <span class="code-editor-op">&gt;</span> <span class="code-editor-const">MAX_LENGTH</span><span class="code-editor-punct">)</span> <span class="code-editor-punct">{</span></span> <span class="code-editor-fold" role="img" aria-label="3 folded lines">⋯</span> <span class="code-editor-punct">}</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">10</span><span class="code-editor-text"><span class="code-editor-sel"><span class="code-editor-indent">  </span><span class="code-editor-kw">return</span> <span class="code-editor-fn code-editor-deprecated">escape</span><span class="code-editor-punct">(</span><span class="code-editor-var">label</span><span class="code-editor-punct">);</span></span> <span class="code-editor-cm">/* escape() is deprecated */</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">11</span><span class="code-editor-text"><span class="code-editor-punct">}</span><span class="code-editor-ws" aria-hidden="true">·</span></span></span></pre>
</div></j3w1-code-editor>

Install and register

Use the exact version. If npm publication is pending, install the release tarball from the installation guide.

npm install --save-exact @j3w1/ui@1.1.0
import '@j3w1/ui/tokens.css';
import '@j3w1/ui/styles/code-editor.css';
import '@j3w1/ui/register/code-editor';

Insert the maintained markup above after loading these imports. Its native children provide accessible names and form behavior; the empty custom tag alone does not create a control.

Vue SFC, React, Astro and plain HTML integration →

Copy the complete implementation

npx --package=@j3w1/ui@1.1.0 j3w1-ui copy code-editor --out ./vendor/j3w1/code-editor

The CLI verifies every file before writing and refuses overwrites. Serve the output over HTTP and open index.html. Keep its modules, CSS, tokens and licence notices together.

Public API

attributes

NameType / signatureMeaning
disabledCustomEventDisable the native controls owned by this instance.
loadingCustomEventMark the primary control busy and prevent duplicate activation.

properties

NameType / signatureMeaning
disabledbooleanDelegates disabled state to native children.
namestringNative control name; application form keys remain explicit.

methods

NameType / signatureMeaning
refresh(): voidReconnect behavior after replacing native child structure.
focus(options?: FocusOptions): voidFocus the first usable native control.
setText(text: string): voidReplace the bounded read-only text view; no code or shell execution occurs.

events

NameType / signatureMeaning
j3w1-actionCustomEventRequest a developer-view action; editing and execution belong to the host.

Dependencies and verification

Registered components: none. Style dependencies: none.

Package presence establishes an available implementation. Actual passes depend on the tested artifact and environment. Read specification execution evidence; read packed-consumer evidence; inspect the complete state matrix.

Full implementation manifest, API and token dependency closure

Canonical specification

Purpose

The reference rendering of source code: the surface, gutter, caret, current line, selection, bracket match, indent guides, rendered whitespace, fold markers and the seventeen monochrome syntax roles. It is the contract a real editor’s theme API is mapped onto; this component never edits.

Anatomy

Root .code-editor; parts .code-editor-<part>: -pre (the <pre>), -line, -ln (line number), -text, -indent, -ws (rendered whitespace), -caret, -sel (selection range), -bracket, -fold, and the gutter fold glyph -fold-gutter. Syntax spans are .code-editor-<role>: -kw keyword, -str string, -cm comment, -num number, -const constant, -fn function, -var variable, -type type, -op operator, -punct punctuation, -prop property, -heading heading, -tag tag, -attr attribute, -esc escape, -invalid invalid, -deprecated deprecated. Line numbers are color.code.line-number on color.code.gutter-bg, separated from the text by a 1px color.code.gutter-rule; indent guides are 1px color.code.indent-guide; rendered whitespace is color.code.whitespace. Every colour is a role variable, so the profile switch on the specimen recolours the sample without touching the markup.

States

StateVisualNon-colour channel
defaultcode on color.code.bg; comments italic; invalid text wavy-underlined in color.code.syntax.invalid; deprecated text struck throughitalic, wavy underline, line-through
focus-visiblecontainer ring 2px color.interaction.focus.ring-container at −3px; a 1px color.code.caret bar; the caret line filled color.code.current-line across gutter and text; its number in color.code.line-number-activethe ring; the caret bar
selectedthe range filled color.code.selection-bg; syntax colours keptthe range is announced through the host’s selection; the fill spans line ends
selected+container-inactivethe same range in color.interaction.selection.inactive-bg with color.interaction.selection.inactive-text; no caret, no ringlightness drop between the two fills
currentboth characters of the matched pair outlined 1px solid color.code.bracket-matchthe outline is a box, not a colour change
read-onlygutter drops to color.surface.sunken behind the same line numbers; the gutter rule becomes dotted; no caret is drawnaria-readonly; dotted rule; missing caret

Precedence: selection is drawn under the text, the current line under the selection, the bracket outline and the caret above everything. The caret never blinks in the reference; a host that blinks it keeps the user’s cursor-blink setting and stops under reduced motion.

Keyboard

The host editor owns every binding. The theme states only what it draws in response: the caret and current line while focused, the selection fill for the host’s selection, the outline for the host’s bracket match. The reference markup is one Tab stop and, being read-only, consumes no key. A host that captures Tab for indentation must offer an escape route (Escape, then Tab) and announce it; the theme does not provide one.

Accessibility

Syntax colour is decoration: no meaning is carried by colour alone. Invalid text has the wavy underline, deprecated text the line-through, comments the italic. Selection is a fill and focus is a ring, drawn together and never substituted for one another. Every syntax role reaches 4.5:1 on color.code.bg (the lowest is color.code.syntax.property at 4.69:1) and color.text.default reaches 7.02:1 inside the selection fill. Comment, type, keyword and property text drop below 4.5:1 inside color.code.selection-bg (3.81–4.38:1); this is a recorded limitation of the selection value and is not to be fixed by recolouring the tokens locally. Line numbers are metadata people read and use color.text.subtle, not a graphic colour; indent guides, rendered whitespace and the gutter rule are decorative. In forced-colours mode the caret, selection and bracket outline take the system Highlight, and the syntax spans keep their text styles.

Portability

A real editor is a host surface: CodeMirror, Monaco, GtkSourceView, KSyntaxHighlighting, an .icls scheme or a terminal editor each expose a theme API, and the port maps the roles here onto that API without touching caret behaviour, keyboard handling or selection semantics. Where a host has no bracket-match outline it may use the bold weight and records the deviation; where a host cannot draw a 1px caret it uses its own caret in color.code.caret. The heritage-ansi and extended profiles change only role values; the mapping does not change.

Non-examples

Rainbow syntax in the default profile (the three extension hues are proposed only in extended). A blinking or animated caret in the reference. A selection that recolours the text to white and loses the syntax roles. A current-line highlight brighter than the selection. A bracket match drawn as a background fill, which reads as a selection. Line numbers in the decorative #7d1310. A glow, gradient or rounded corner on the editor box. Removing the host’s focus indicator without drawing the container ring.

j3w1 / command

  • SPEC Overview
  • SPEC Foundations
  • SPEC Components
  • SPEC Patterns
  • SPEC Tokens
  • SPEC Tools
  • SPEC Implement
  • SPEC For agents
  • SPEC Ports
  • SPEC Releases
  • DEMO Vue application demo
  • SPEC Complete UI theme specification