Skip to content

Commit 3a2027e

Browse files
committed
feat: add custom CodeMirror theme with purple accent colors
Replace oneDark theme with custom crispyEditorTheme featuring: - Purple accent (#c9a6eb) for keywords and HTML tags - Matching dark background (#262630) - Applied consistently across editor and reference code blocks 🤖 Generated with [Claude Code](https://claude.com/claude-code)
1 parent d0d8c5a commit 3a2027e

3 files changed

Lines changed: 82 additions & 8 deletions

File tree

src/app.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { LessonEngine } from "./impl/LessonEngine.js";
2-
import { CodeEditor } from "./impl/CodeEditor.js";
2+
import { CodeEditor, crispyEditorTheme } from "./impl/CodeEditor.js";
33
import { renderLesson, renderModuleList, renderLevelIndicator, updateActiveLessonInSidebar } from "./helpers/renderer.js";
44
import { loadModules } from "./config/lessons.js";
55
import { initI18n, t, getLanguage, setLanguage, applyTranslations } from "./i18n.js";
@@ -10,7 +10,6 @@ import { getRandomTemplate } from "./config/playground-templates.js";
1010
// CodeMirror imports for syntax highlighting
1111
import { EditorState } from "@codemirror/state";
1212
import { EditorView } from "@codemirror/view";
13-
import { oneDark } from "@codemirror/theme-one-dark";
1413
import { html } from "@codemirror/lang-html";
1514
import { css } from "@codemirror/lang-css";
1615

@@ -79,7 +78,7 @@ function highlightSectionCodeBlocks() {
7978
// Create read-only CodeMirror view
8079
const state = EditorState.create({
8180
doc: content,
82-
extensions: [langExtension, oneDark, readOnlyTheme, EditorState.readOnly.of(true), EditorView.lineWrapping]
81+
extensions: [langExtension, crispyEditorTheme, readOnlyTheme, EditorState.readOnly.of(true), EditorView.lineWrapping]
8382
});
8483

8584
const view = new EditorView({
@@ -2153,7 +2152,7 @@ function highlightReferenceCodeBlocks() {
21532152
const view = new EditorView({
21542153
state: EditorState.create({
21552154
doc: code,
2156-
extensions: [isHtml ? html() : css(), oneDark, readOnlyTheme, EditorState.readOnly.of(true), EditorView.editable.of(false)]
2155+
extensions: [isHtml ? html() : css(), crispyEditorTheme, readOnlyTheme, EditorState.readOnly.of(true), EditorView.editable.of(false)]
21572156
}),
21582157
parent
21592158
});

src/i18n.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ const translations = {
253253

254254
// Landing page
255255
landingHeroTitle: "Web Programmierung",
256-
landingHeroHighlight: "Selbstständig Coden lernen",
256+
landingHeroHighlight: "Selbstständig lernen",
257257
landingHeroSubtitle: "Meistere HTML, CSS und Tailwind durch praktische Übungen mit sofortigem Feedback. Kostenlos und Open Source.",
258258
landingCtaStart: "Jetzt starten",
259259
landingWhyTitle: "Warum Code Crispies funktioniert",

src/impl/CodeEditor.js

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,88 @@ import { EditorState, Prec } from "@codemirror/state";
55
import { EditorView, keymap, placeholder } from "@codemirror/view";
66
import { defaultKeymap, historyKeymap, indentMore, indentLess, undo, redo } from "@codemirror/commands";
77
import { history } from "@codemirror/commands";
8-
import { oneDark } from "@codemirror/theme-one-dark";
98
import { html } from "@codemirror/lang-html";
109
import { css } from "@codemirror/lang-css";
1110
import { autocompletion } from "@codemirror/autocomplete";
1211
import { abbreviationTracker, expandAbbreviation } from "@emmetio/codemirror6-plugin";
12+
import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
13+
import { tags } from "@lezer/highlight";
1314

14-
// Custom overrides for One Dark theme
15+
// Custom theme with purple accent colors (matching app completed state)
16+
const crispyTheme = EditorView.theme(
17+
{
18+
"&": {
19+
backgroundColor: "#262630",
20+
color: "#c8c8d0"
21+
},
22+
".cm-content": {
23+
caretColor: "#9b6dd4"
24+
},
25+
".cm-cursor, .cm-dropCursor": {
26+
borderLeftColor: "#9b6dd4"
27+
},
28+
"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": {
29+
backgroundColor: "#3e3e4a"
30+
},
31+
".cm-panels": {
32+
backgroundColor: "#262630",
33+
color: "#c8c8d0"
34+
},
35+
".cm-searchMatch": {
36+
backgroundColor: "#3e3e4a",
37+
outline: "1px solid #9b6dd4"
38+
},
39+
".cm-searchMatch.cm-searchMatch-selected": {
40+
backgroundColor: "rgba(155, 109, 212, 0.3)"
41+
},
42+
".cm-activeLine": {
43+
backgroundColor: "#2e2e3a"
44+
},
45+
".cm-selectionMatch": {
46+
backgroundColor: "#3e3e4a"
47+
},
48+
".cm-gutters": {
49+
backgroundColor: "#262630",
50+
color: "#808090",
51+
border: "none"
52+
},
53+
".cm-activeLineGutter": {
54+
backgroundColor: "#2e2e3a"
55+
},
56+
".cm-lineNumbers .cm-gutterElement": {
57+
color: "#808090"
58+
}
59+
},
60+
{ dark: true }
61+
);
62+
63+
// Syntax highlighting with purple accent
64+
const crispyHighlight = HighlightStyle.define([
65+
{ tag: tags.keyword, color: "#c9a6eb" },
66+
{ tag: tags.operator, color: "#cdd6f4" },
67+
{ tag: tags.variableName, color: "#89b4fa" },
68+
{ tag: tags.propertyName, color: "#89b4fa" },
69+
{ tag: tags.attributeName, color: "#89b4fa" },
70+
{ tag: tags.className, color: "#89b4fa" },
71+
{ tag: tags.tagName, color: "#c9a6eb" },
72+
{ tag: tags.string, color: "#a6e3a1" },
73+
{ tag: tags.number, color: "#fab387" },
74+
{ tag: tags.bool, color: "#fab387" },
75+
{ tag: tags.null, color: "#fab387" },
76+
{ tag: tags.comment, color: "#6c7086", fontStyle: "italic" },
77+
{ tag: tags.bracket, color: "#cdd6f4" },
78+
{ tag: tags.punctuation, color: "#cdd6f4" },
79+
{ tag: tags.definition(tags.variableName), color: "#89b4fa" },
80+
{ tag: tags.function(tags.variableName), color: "#89b4fa" },
81+
{ tag: tags.atom, color: "#c9a6eb" },
82+
{ tag: tags.unit, color: "#a6e3a1" },
83+
{ tag: tags.color, color: "#f9e2af" }
84+
]);
85+
86+
// Combined theme export
87+
export const crispyEditorTheme = [crispyTheme, syntaxHighlighting(crispyHighlight)];
88+
89+
// Custom overrides for editor styling
1590
const editorTheme = EditorView.theme(
1691
{
1792
"&": {
@@ -51,7 +126,7 @@ export class CodeEditor {
51126
// Build extensions array
52127
const extensions = [
53128
langExtension,
54-
oneDark,
129+
crispyEditorTheme,
55130
editorTheme,
56131
// History for undo/redo
57132
history(),

0 commit comments

Comments
 (0)