v1.2.0
  1. ds
  2. Components
  3. Code editor

Code editor

A mono textarea with line numbers, error lines and a problems list.

  • 2 problems
Show code
<div class="editor" style="height:15rem">
  <div class="editor-gutter" aria-hidden="true">
    <span>1</span><span>2</span>…<span class="err">5</span><span>6</span><span class="warn">7</span>
  </div>
  <div class="editor-area">
    <div class="editor-marks" aria-hidden="true">
      <i class="err" style="--line:5"></i><i class="warn" style="--line:7"></i>
    </div>
    <textarea class="editor-input" wrap="off" spellcheck="false" aria-label="Crontab">…</textarea>
  </div>
  <ul class="editor-errors" aria-label="Problems">
    <li class="label">2 problems</li>
    <li><button class="editor-err" type="button"><code>5:8</code><span>Hour 25 is out of range (0–23)</span></button></li>
    <li><button class="editor-err warn" type="button"><code>7:21</code><span>No command after the schedule</span></button></li>
  </ul>
</div>

The surface lab and ack use for text input: a mono textarea with line numbers, line highlights for errors and warnings, and the problems listed under the text. This one checks a crontab as you type. Fix the hour on line 5, or click a problem to jump to it.

It is styling only. Tokenising, highlighting and validation belong to the product, and so does the script that keeps the three layers in step. What ds.css guarantees is that they line up: the gutter, the marks and the text share one line height and one top padding, in rem, at every root size.

Keeping the layers in step

// Scroll: the gutter and the marks follow the textarea.
input.addEventListener('scroll', () => editor.style.setProperty('--scroll', input.scrollTop));

// After each edit: one gutter row per line, one mark per line with a problem.
gutter.innerHTML = lines.map((_, i) => `<span class="${sev(i + 1)}">${i + 1}</span>`).join('');
marks.innerHTML = problems.map((p) => `<i class="${p.sev}" style="--line:${p.line}"></i>`).join('');

The textarea does not wrap (white-space: pre, plus wrap="off" for Safari), so line n is always row n and the numbers never drift. Long lines scroll sideways.

API

ClassOnWhat it does
.editordivThe frame: gutter, text and problems. Give it a height, or set rows on the textarea.
--lh / --pad.editorLine height (1.375rem) and top padding (0.625rem), shared by all three layers.
--scroll.editorThe textarea's scrollTop, unitless. The gutter and marks move with it.
.editor-gutterdivLine numbers, one span per line. .on for the caret line, .err and .warn.
.editor-areadivHolds the marks and the textarea.
.editor-marksdivHighlights under the text: <i class="err" style="--line:5">. Also .warn and .on.
.editor-inputtextareaMono, no wrapping, transparent so the marks show through, accent caret.
.editor-errorsulProblems under the text. Hidden when empty. A li.label can head it.
.editor-errbuttonOne problem: a dot, the position in <code>, the message. .warn for a warning.

Accessibility

  • The gutter and marks are aria-hidden. Point aria-describedby from the textarea at the problems list, so the count and messages are reachable.
  • Each problem is a button that moves the caret to its line and column.
  • Tab types a tab in most code editors. If you do that, leave Escape then Tab as the way out, and say so near the editor.