Skip to content

softwarity/interactive-code

Repository files navigation

@softwarity/interactive-code

A Web Component for displaying syntax-highlighted code with interactive bindings. Perfect for documentation, tutorials, and live demos.

Features

  • Syntax Highlighting: HTML, SCSS, TypeScript, Shell, and JSON
  • Interactive Bindings: Click to edit values directly in the code
  • Multiple Types: boolean, number, string, select, color, comment, attribute, button
  • Collapsible Sections: Fold non-interactive line ranges with ${fold} markers (copy/download stay complete)
  • Theme System: Built-in IntelliJ default + 4 external CSS themes (vscode, github, solarized, catppuccin) with light/dark variants
  • Mixed Content Highlighting: HTML with embedded <style> (SCSS) and <script> (TypeScript) blocks
  • Copy & Download: Optional copy and download buttons (valid JSON export)
  • Line Numbers: Optional gutter line numbers
  • Accessibility: ARIA attributes, keyboard navigation (Enter/Space, ArrowUp/Down)
  • Framework Agnostic: Works with Angular, React, Vue, or vanilla JS
  • Zero Dependencies: Pure Web Components

Demo

Live demo

Installation

npm

npm install @softwarity/interactive-code
import '@softwarity/interactive-code';

CDN

<script type="module" src="https://cdn.jsdelivr.net/npm/@softwarity/interactive-code"></script>

No build step required — the custom elements <interactive-code> and <code-binding> are registered automatically.

Usage

<interactive-code language="scss">
  <textarea>
:root {
  --width: ${width}px;
  --enabled: ${enabled};
}
  </textarea>
  <code-binding key="width" type="number" value="72" min="48" max="120"></code-binding>
  <code-binding key="enabled" type="boolean" value="true"></code-binding>
</interactive-code>

Escaping: write \${...} to display a literal ${...} in the code instead of interpreting it as a binding or fold marker.

Binding Types

Type Description Interaction
boolean true/false value Click to toggle
number Numeric value Click to edit, supports min/max/step
string Text value Click to edit
select Option from list Click to toggle (2 options), dropdown (3+), or carousel (carousel attribute)
color Color value Click to open color picker
comment Line/block toggle Click indicator to comment/uncomment (//, #, <!-- -->, /* */)
attribute HTML attribute toggle Click to toggle (strikethrough when disabled)
button Action token (value = label) Click to fire a change event (e.detail = value); no value edit, no re-render
readonly Display only No interaction

API

<interactive-code>

Attribute Type Description
language 'html' | 'scss' | 'typescript' | 'shell' | 'json' Syntax highlighting language
color-scheme 'light' | 'dark' Color scheme override (inherits from parent by default)
show-separators boolean Show visual separators between textarea sections
show-copy boolean Show copy-to-clipboard button (top-right corner)
show-download boolean Show download button (exports the full content as a file)
download string File name for the download button (defaults to snippet.<ext>)
show-line-numbers boolean Show line numbers in the gutter
Property Type Description
code string | null Set code content programmatically

<code-binding>

Attribute Type Description
key string Binding identifier (matches ${key} in template)
type BindingType Type of binding
value any Initial value
disabled boolean Disable editing
min number Minimum value (for number type)
max number Maximum value (for number type)
step number Step increment (for number type)
options string Comma-separated options (for select type)
carousel boolean Cycle through options on click instead of dropdown (for select type)
Event Description
change Fired when value changes (CustomEvent with detail = new value)

Inline handler: Use onchange attribute where e is the CustomEvent:

<code-binding key="checked" type="boolean" value="true"
  onchange="document.getElementById('preview').checked = e.detail"></code-binding>

addEventListener / Framework binding:

// Vanilla JS
binding.addEventListener('change', (e) => {
  preview.checked = e.detail;
});

// Angular: (change)="handler($event.detail)"
// React: onChange={(e) => handler(e.detail)}
// Vue: @change="handler($event.detail)"

Examples

Boolean Toggle

<interactive-code language="html">
  <textarea>
<nav [autoCollapse]="${autoCollapse}">...</nav>
  </textarea>
  <code-binding key="autoCollapse" type="boolean" value="true"></code-binding>
</interactive-code>

Number with Constraints

<interactive-code language="scss">
  <textarea>
:root {
  --width: ${width}px;
}
  </textarea>
  <code-binding key="width" type="number" value="72" min="48" max="120" step="4"></code-binding>
</interactive-code>

Color Picker

<interactive-code language="scss">
  <textarea>
:root {
  --primary: ${primary};
}
  </textarea>
  <code-binding key="primary" type="color" value="#6750a4"></code-binding>
</interactive-code>

Comment Toggle (Line Enable/Disable)

Comment style adapts to language: // for TypeScript/SCSS, # for Shell, <!-- --> for HTML.

<interactive-code language="scss">
  <textarea>
:root {
  ${enableWidth}--custom-width: 280px;
}
  </textarea>
  <code-binding key="enableWidth" type="comment" value="true"></code-binding>
</interactive-code>

Block Comment

Use ${key}...${/key} syntax for multi-line or inline block comments:

<interactive-code language="typescript">
  <textarea>
const config = {
  name: 'app',
  ${debug}debug: true,
  verbose: true,${/debug}
};
  </textarea>
  <code-binding key="debug" type="comment" value="true"></code-binding>
</interactive-code>

Attribute Toggle

Toggle HTML attributes on/off. Supports attributes with or without values:

<interactive-code language="html">
  <textarea>
<button ${disabled}>Submit</button>
<input ${placeholder}="Enter name" ${required}>
  </textarea>
  <code-binding key="disabled" type="attribute" value="true"></code-binding>
  <code-binding key="placeholder" type="attribute" value="true"></code-binding>
  <code-binding key="required" type="attribute" value="false"></code-binding>
</interactive-code>

Conditional Textareas

Show different code sections based on binding values. Multiple <textarea> elements are concatenated, and the condition attribute controls visibility:

<interactive-code language="typescript" show-separators>
  <textarea>const result = provider.complete(input, { groupBy: ${groupBy} });</textarea>
  <textarea condition="!groupBy">// Use result.items for flat list
console.log(result.items);</textarea>
  <textarea condition="groupBy">// Use result.groups for grouped display
console.log(result.groups);</textarea>
  <code-binding key="groupBy" type="select" options="undefined,'continent'" value="undefined"></code-binding>
</interactive-code>
  • condition="key" - Show when binding value is truthy
  • condition="!key" - Show when binding value is falsy
  • condition="key=value" - Show when binding value equals a specific value
  • condition="!key=value" - Show when binding value does NOT equal a specific value
  • show-separators - Add visual separators between sections (customizable via --code-separator-color)

Collapsible Sections

Wrap a range of lines in ${fold}${/fold} markers to make it foldable (GitHub-diff style). Collapsed by default; use ${fold:open} to start expanded. The marker lines are removed from the output — folding is purely visual, and copy/download still export the full content. Works inside a <textarea> or via the code property, in any language.

<interactive-code language="json" show-download download="config.json">
  <textarea>{
  "name": "${name}",
${fold}
  "_internal": {
    "trace": true,
    "buffer": 4096
  },
${/fold}
  "enabled": ${enabled}
}</textarea>
  <code-binding key="name" type="string" value="app"></code-binding>
  <code-binding key="enabled" type="boolean" value="true"></code-binding>
</interactive-code>

Action Button

A button binding is a clickable token that fires a change event on every click (no value to edit, no re-render), with e.detail set to its value — handy for a hub of actions.

<interactive-code language="typescript">
  <textarea>await provider.${refresh}();</textarea>
  <code-binding key="refresh" type="button" value="refresh()"
    onchange="runAction(e.detail)"></code-binding>
</interactive-code>

Themes

The built-in default is IntelliJ (Light/Darcula). Four external CSS themes are available as separate stylesheets:

Theme File Light Dark
VS Code themes/vscode.css Light+ Dark+
GitHub themes/github.css Light Dark
Solarized themes/solarized.css Light Dark
Catppuccin themes/catppuccin.css Latte Mocha

Load a theme by adding a <link> stylesheet:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@softwarity/interactive-code/themes/vscode.css">

Use color-scheme to override light/dark mode per element:

<interactive-code language="typescript" color-scheme="light">
  ...
</interactive-code>

CSS Customization

The component exposes CSS custom properties for styling. Themes and custom overrides use these variables.

UI Variables

Property Description
--code-bg Background color
--code-text Foreground text color
--code-border-radius Border radius
--code-line-number Line number color
--code-gutter-width Width of the left gutter control column
--code-separator-color Separator color between textarea sections
--code-focus-outline Focus ring color
--code-input-bg Inline input background
--code-input-border Inline input border
--code-hover-bg Hover background
--code-copy-color Copy button color
--code-copy-border Copy button border
--code-copy-accent Copy success accent
--code-color-preview-border Color swatch border
--code-interactive-highlight Interactive zone accent color
--code-interactive-color Interactive zone text color
--code-interactive-bg-color Interactive zone background
--code-interactive-border-color Interactive zone border color
--code-comment-color Comment indicator color

Token Variables

All syntax token colors: --token-keyword, --token-string, --token-number, --token-comment, --token-tag, --token-attr-name, --token-attr-value, --token-punctuation, --token-property, --token-variable, --token-function, --token-decorator, --token-type, --token-class-name, --token-template-string, --token-value, --token-unknown, --token-binding-key

Interactive Zone Styling

Interactive controls expose part="interactive" for external CSS styling:

interactive-code::part(interactive) {
  text-decoration: underline wavy var(--code-interactive-highlight);
}
interactive-code::part(interactive):hover {
  background: var(--code-interactive-bg-color);
}

Built-in styles: wavy (default), dotted, dashed, highlight, outline, pill, hand-drawn, none.

interactive-code {
  --code-bg: #282c34;
  --code-border-radius: 4px;
  --code-separator-color: rgba(100, 100, 100, 0.5);
}

Development

# Install dependencies
npm install

# Start dev server
npm run dev

# Build for production
npm run build

License

MIT - Softwarity