Skip to content

Commit d7a91a1

Browse files
committed
feat: add theme support and enhance interactive code element
- Added support for multiple themes (Catppuccin, GitHub, Solarized, VS Code) with CSS variables for token colors. - Introduced a color-scheme attribute to the InteractiveCodeElement for light/dark mode. - Updated the rendering logic to handle mixed content highlighting for HTML, including <style> and <script> blocks. - Enhanced the CSS to use light-dark() function for better theme adaptability. - Added tests to verify theme functionality and mixed content highlighting behavior.
1 parent 482ad82 commit d7a91a1

10 files changed

Lines changed: 626 additions & 57 deletions

RELEASE_NOTES.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
## 1.0.7
44

5+
### Features
6+
7+
- **Theme system**: Built-in IntelliJ default theme with 4 external CSS themes (vscode, github, solarized, catppuccin). Themes use CSS custom properties with `light-dark()` for automatic light/dark support. External themes are published as CSS files and available via CDN.
8+
- **Color scheme**: `color-scheme` attribute to override light/dark mode per element (inherits from parent by default).
9+
- **Mixed content highlighting**: When `language="html"`, `<style>` blocks use SCSS highlighting and `<script>` blocks use TypeScript highlighting automatically.
10+
- **CSS custom properties**: All 18 token colors and 10 UI colors exposed as `--token-*` and `--code-*` variables for external customization.
11+
512
### Bug Fixes
613

714
- **Conditional content not updated for inline controls**: When a select (3+ options), number, string, or color binding was modified via inline controls, the `_internalChange` flag prevented `updateCode()` from being called, so conditional textareas were not re-evaluated. Now checks for condition dependencies after inline changes.
@@ -25,7 +32,8 @@
2532

2633
### Tests
2734

28-
- Added 26 new tests: cleanup (3), XSS (3), conditional inline (1), copy button (8), line numbers (5), accessibility (6) — 136 tests total
35+
- Added 26 new tests: cleanup (3), XSS (3), conditional inline (1), copy button (8), line numbers (5), accessibility (6)
36+
- Updated tests: theme system (7), mixed content highlighting (7) — 150 tests total
2937

3038
---
3139

demo/index.html

Lines changed: 172 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
<!DOCTYPE html>
2-
<html lang="en">
2+
<html lang="en" class="dark">
33
<head>
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
66
<title>@softwarity/interactive-code</title>
77
<link rel="icon" type="image/svg+xml" href="https://www.softwarity.io/img/softwarity.svg">
88
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
99
<style>
10+
/* Color scheme toggle */
11+
html.dark { color-scheme: dark; }
12+
html:not(.dark) { color-scheme: light; }
13+
1014
:root {
11-
color-scheme: dark;
1215
--bg-gradient: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
1316
--bg-primary: #161b22;
1417
--bg-secondary: #21262d;
@@ -286,10 +289,43 @@
286289
color: var(--text-muted);
287290
}
288291

292+
/* Force IntelliJ dark theme on "Written by developer" code blocks */
289293
interactive-code.inline-code {
290294
margin: 0;
295+
color-scheme: dark;
291296
--code-border-radius: 0 0 6px 6px;
292-
--code-bg: #1e1e1e;
297+
--code-bg: #2b2d30;
298+
--code-text: #a9b7c6;
299+
--token-keyword: #cc7832;
300+
--token-string: #6a8759;
301+
--token-number: #6897bb;
302+
--token-comment: #808080;
303+
--token-tag: #e8bf6a;
304+
--token-attr-name: #bababa;
305+
--token-attr-value: #6a8759;
306+
--token-punctuation: #a9b7c6;
307+
--token-property: #9876aa;
308+
--token-variable: #a9b7c6;
309+
--token-function: #ffc66d;
310+
--token-decorator: #bbb529;
311+
--token-type: #769aa5;
312+
--token-class-name: #769aa5;
313+
--token-template-string: #6a8759;
314+
--token-value: #a9b7c6;
315+
--token-unknown: #769aa5;
316+
--token-binding-key: #769aa5;
317+
--code-line-number: rgba(255,255,255,0.25);
318+
--code-separator-color: rgba(255,255,255,0.1);
319+
--code-focus-outline: #214283;
320+
--code-input-bg: #313335;
321+
--code-input-border: rgba(255,255,255,0.2);
322+
--code-hover-bg: rgba(255,255,255,0.1);
323+
--code-copy-color: rgba(255,255,255,0.5);
324+
--code-copy-border: rgba(255,255,255,0.2);
325+
--code-copy-accent: #769aa5;
326+
--code-color-preview-border: rgba(255,255,255,0.3);
327+
--code-editable-underline: #769aa5;
328+
--code-comment-color: #808080;
293329
border: 1px solid var(--border-color);
294330
border-top: none;
295331
border-radius: 0 0 6px 6px;
@@ -453,6 +489,28 @@ <h1>@softwarity/interactive-code</h1>
453489
Supports HTML, SCSS, TypeScript, and Shell with click-to-edit values.
454490
</p>
455491

492+
<!-- Theme & Color Scheme -->
493+
<section>
494+
<h2>Theme & Color Scheme</h2>
495+
<p>Click on highlighted values to change theme and color scheme. Changes apply globally to all "Seen by user" code blocks below.</p>
496+
497+
<interactive-code class="inline-code" language="html">
498+
<textarea>${useTheme}<link rel="stylesheet" href="https://unpkg.com/@softwarity/interactive-code/themes/${theme}.css"></textarea>
499+
<code-binding key="useTheme" type="comment" value="true"
500+
onchange="loadThemeCSS(e.detail ? this.closest('interactive-code').querySelector('[key=theme]').value : '')"></code-binding>
501+
<code-binding key="theme" type="select" options="vscode,github,solarized,catppuccin" value="catppuccin"
502+
onchange="var c=this.closest('interactive-code').querySelector('[key=useTheme]'); if(String(c.value)!=='false') loadThemeCSS(e.detail)"></code-binding>
503+
</interactive-code>
504+
505+
<interactive-code class="inline-code" language="scss">
506+
<textarea>html {
507+
color-scheme: ${colorScheme};
508+
}</textarea>
509+
<code-binding key="colorScheme" type="select" options="dark,light" value="dark"
510+
onchange="document.documentElement.classList.toggle('dark', e.detail === 'dark'); document.querySelectorAll('.user-view interactive-code').forEach(function(el) { el.setAttribute('color-scheme', e.detail) })"></code-binding>
511+
</interactive-code>
512+
</section>
513+
456514
<!-- Playground Section -->
457515
<section class="playground">
458516
<h2>Playground</h2>
@@ -891,6 +949,96 @@ <h3>Usage</h3>
891949
</interactive-code>
892950
</article>
893951

952+
<!-- Themes -->
953+
<article class="doc-section">
954+
<h3>Themes</h3>
955+
<p>The component uses an IntelliJ-based theme by default. To apply a different theme, load an external CSS file:</p>
956+
957+
<h4>Via CDN</h4>
958+
<interactive-code language="html">
959+
<textarea>
960+
<!-- Pick one -->
961+
<link rel="stylesheet" href="https://unpkg.com/@softwarity/interactive-code/themes/${theme}.css"></textarea>
962+
<code-binding key="theme" type="select" options="vscode,github,solarized,catppuccin" value="vscode"></code-binding>
963+
</interactive-code>
964+
965+
<h4>Via npm import</h4>
966+
<interactive-code language="html">
967+
<textarea>
968+
<link rel="stylesheet" href="node_modules/@softwarity/interactive-code/themes/${theme}.css"></textarea>
969+
<code-binding key="theme" type="select" options="vscode,github,solarized,catppuccin" value="vscode"></code-binding>
970+
</interactive-code>
971+
972+
<h4>Available themes</h4>
973+
<div class="api-table-wrapper">
974+
<table class="api-table">
975+
<thead>
976+
<tr>
977+
<th>Theme</th>
978+
<th>File</th>
979+
<th>Light variant</th>
980+
<th>Dark variant</th>
981+
</tr>
982+
</thead>
983+
<tbody>
984+
<tr>
985+
<td><strong>IntelliJ</strong></td>
986+
<td><em>built-in (default)</em></td>
987+
<td>IntelliJ Light</td>
988+
<td>Darcula</td>
989+
</tr>
990+
<tr>
991+
<td>VS Code</td>
992+
<td><code>themes/vscode.css</code></td>
993+
<td>Light+</td>
994+
<td>Dark+</td>
995+
</tr>
996+
<tr>
997+
<td>GitHub</td>
998+
<td><code>themes/github.css</code></td>
999+
<td>GitHub Light</td>
1000+
<td>GitHub Dark</td>
1001+
</tr>
1002+
<tr>
1003+
<td>Solarized</td>
1004+
<td><code>themes/solarized.css</code></td>
1005+
<td>Solarized Light</td>
1006+
<td>Solarized Dark</td>
1007+
</tr>
1008+
<tr>
1009+
<td>Catppuccin</td>
1010+
<td><code>themes/catppuccin.css</code></td>
1011+
<td>Latte</td>
1012+
<td>Mocha</td>
1013+
</tr>
1014+
</tbody>
1015+
</table>
1016+
</div>
1017+
1018+
<h4>Color scheme</h4>
1019+
<p>The component inherits <code>color-scheme</code> from its parent (typically the page). Each theme includes both light and dark values via CSS <code>light-dark()</code>. You can override per element:</p>
1020+
<interactive-code language="html">
1021+
<textarea>
1022+
<!-- Force light mode on a specific block -->
1023+
<interactive-code language="scss" color-scheme="light">
1024+
...
1025+
</interactive-code></textarea>
1026+
</interactive-code>
1027+
1028+
<h4>Custom theme</h4>
1029+
<p>Create your own theme by setting CSS custom properties on <code>interactive-code</code>:</p>
1030+
<interactive-code language="scss">
1031+
<textarea>
1032+
interactive-code {
1033+
--code-bg: light-dark(#fafafa, #1a1a2e);
1034+
--code-text: light-dark(#333, #eee);
1035+
--token-keyword: light-dark(#d63384, #f0a8c4);
1036+
--token-string: light-dark(#198754, #75d5a0);
1037+
/* ... see full list in theme files */
1038+
}</textarea>
1039+
</interactive-code>
1040+
</article>
1041+
8941042
<!-- API - InteractiveCodeElement -->
8951043
<article class="doc-section">
8961044
<h3>API - &lt;interactive-code&gt;</h3>
@@ -930,6 +1078,12 @@ <h3>API - &lt;interactive-code&gt;</h3>
9301078
<td><code>false</code></td>
9311079
<td>Show the copy-to-clipboard button</td>
9321080
</tr>
1081+
<tr>
1082+
<td><code>color-scheme</code></td>
1083+
<td><code>'light' | 'dark'</code></td>
1084+
<td><em>inherited</em></td>
1085+
<td>Override color scheme (inherits from parent by default)</td>
1086+
</tr>
9331087
</tbody>
9341088
</table>
9351089
</div>
@@ -1134,5 +1288,20 @@ <h3>Features</h3>
11341288

11351289
<script type="module" src="../src/index.ts"></script>
11361290
<script type="module" src="https://unpkg.com/@softwarity/projects"></script>
1291+
<script>
1292+
function loadThemeCSS(theme) {
1293+
var existing = document.getElementById('theme-css');
1294+
if (existing) existing.remove();
1295+
if (!theme) return;
1296+
var link = document.createElement('link');
1297+
link.id = 'theme-css';
1298+
link.rel = 'stylesheet';
1299+
link.href = '../themes/' + theme + '.css';
1300+
document.head.appendChild(link);
1301+
}
1302+
1303+
// Load initial theme (catppuccin)
1304+
loadThemeCSS('catppuccin');
1305+
</script>
11371306
</body>
11381307
</html>

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"files": [
1818
"dist",
1919
"src",
20+
"themes",
2021
"types"
2122
],
2223
"scripts": {

0 commit comments

Comments
 (0)