or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

editor-commands.mdeditor-core.mdeditor-factory.mdextension-system.mdindex.mdlanguage-support.mdmime-type-service.mdsearch-replace.mdspecial-extensions.mdtheme-system.md
tile.json

theme-system.mddocs/

0

# Theme System

1

2

The theme system provides comprehensive styling and theming capabilities with JupyterLab integration, CSS variable-based themes, and custom theme support.

3

4

## Capabilities

5

6

### EditorThemeRegistry

7

8

Registry for managing editor themes with support for custom themes and JupyterLab integration.

9

10

```typescript { .api }

11

/**

12

* CodeMirror 6 theme registry

13

* Manages themes with JupyterLab integration and CSS variables

14

*/

15

class EditorThemeRegistry implements IEditorThemeRegistry {

16

constructor();

17

18

/**

19

* Get all registered themes

20

*/

21

readonly themes: IEditorTheme[];

22

23

/**

24

* Get the default CodeMirror 6 theme for JupyterLab

25

*/

26

defaultTheme(): Extension;

27

28

/**

29

* Register a new theme

30

*/

31

addTheme(theme: IEditorTheme): void;

32

33

/**

34

* Get a theme by name (falls back to default theme if not found)

35

*/

36

getTheme(name: string): Extension;

37

}

38

39

interface IEditorThemeRegistry {

40

readonly themes: IEditorTheme[];

41

defaultTheme(): Extension;

42

addTheme(theme: IEditorTheme): void;

43

getTheme(name: string): Extension;

44

}

45

```

46

47

**Usage Examples:**

48

49

```typescript

50

import { EditorThemeRegistry } from "@jupyterlab/codemirror";

51

52

// Create theme registry

53

const themes = new EditorThemeRegistry();

54

55

// Get default JupyterLab theme

56

const defaultTheme = themes.defaultTheme();

57

58

// Add custom theme

59

themes.addTheme({

60

name: 'my-dark-theme',

61

displayName: 'My Dark Theme',

62

theme: myDarkThemeExtension

63

});

64

65

// Get theme by name

66

const darkTheme = themes.getTheme('my-dark-theme');

67

const fallbackTheme = themes.getTheme('nonexistent'); // Returns default theme

68

69

// List all themes

70

console.log(themes.themes.map(t => t.name));

71

```

72

73

### Built-in JupyterLab Theme

74

75

Complete JupyterLab-integrated theme with CSS variables and syntax highlighting.

76

77

```typescript { .api }

78

/**

79

* Complete JupyterLab CodeMirror 6 theme

80

* Combines editor theme and syntax highlighting

81

*/

82

const jupyterTheme: Extension;

83

84

/**

85

* JupyterLab editor theme definition with CSS variables

86

*/

87

const jupyterEditorTheme: EditorView.theme;

88

89

/**

90

* Syntax highlighting style for JupyterLab using CSS variables

91

*/

92

const jupyterHighlightStyle: HighlightStyle;

93

```

94

95

**Usage Examples:**

96

97

```typescript

98

import {

99

jupyterTheme,

100

jupyterEditorTheme,

101

jupyterHighlightStyle

102

} from "@jupyterlab/codemirror";

103

import { EditorView } from "@codemirror/view";

104

import { syntaxHighlighting } from "@codemirror/language";

105

106

// Use complete JupyterLab theme

107

const editor = new EditorView({

108

extensions: [

109

jupyterTheme,

110

// ... other extensions

111

],

112

parent: document.body

113

});

114

115

// Use components separately

116

const customTheme = [

117

jupyterEditorTheme,

118

syntaxHighlighting(jupyterHighlightStyle)

119

];

120

121

// Extend JupyterLab theme

122

const extendedTheme = [

123

jupyterTheme,

124

EditorView.theme({

125

// Additional custom styles

126

'.cm-editor': {

127

fontSize: '16px'

128

},

129

'.cm-focused': {

130

outline: '2px solid var(--jp-brand-color1)'

131

}

132

})

133

];

134

```

135

136

### Theme Interface

137

138

Interface for defining custom themes.

139

140

```typescript { .api }

141

/**

142

* Editor theme interface

143

*/

144

interface IEditorTheme {

145

/**

146

* Theme unique identifier

147

*/

148

readonly name: string;

149

150

/**

151

* Theme display name (optional)

152

*/

153

readonly displayName?: string;

154

155

/**

156

* Editor extension for the theme

157

*/

158

readonly theme: Extension;

159

}

160

```

161

162

### Default Themes

163

164

Built-in theme collection with JupyterLab integration.

165

166

```typescript { .api }

167

/**

168

* Get the default editor themes

169

* Returns array including CodeMirror default theme

170

*/

171

static getDefaultThemes(translator?: ITranslator | null): ReadonlyArray<Readonly<IEditorTheme>>;

172

```

173

174

**Usage Examples:**

175

176

```typescript

177

import { EditorThemeRegistry } from "@jupyterlab/codemirror";

178

179

// Get default themes

180

const defaultThemes = EditorThemeRegistry.getDefaultThemes();

181

182

// Create registry with default themes

183

const registry = new EditorThemeRegistry();

184

defaultThemes.forEach(theme => registry.addTheme(theme));

185

186

// Get default themes with translation

187

const translatedThemes = EditorThemeRegistry.getDefaultThemes(translator);

188

```

189

190

### Custom Theme Creation

191

192

Creating custom themes with CSS variables and syntax highlighting.

193

194

```typescript

195

import { EditorView } from "@codemirror/view";

196

import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";

197

import { tags } from "@lezer/highlight";

198

199

// Create custom editor theme

200

const darkEditorTheme = EditorView.theme({

201

'&': {

202

color: '#f8f8f2',

203

backgroundColor: '#272822'

204

},

205

'.cm-content': {

206

padding: '16px',

207

caretColor: '#f92672'

208

},

209

'.cm-focused .cm-cursor': {

210

borderLeftColor: '#f92672'

211

},

212

'.cm-focused .cm-selectionBackground, ::selection': {

213

backgroundColor: '#49483e'

214

},

215

'.cm-panels': {

216

backgroundColor: '#272822',

217

color: '#f8f8f2'

218

},

219

'.cm-searchMatch': {

220

backgroundColor: '#49483e',

221

outline: '1px solid #f92672'

222

},

223

'.cm-activeLine': {

224

backgroundColor: '#49483e22'

225

},

226

'.cm-gutters': {

227

backgroundColor: '#272822',

228

color: '#75715e',

229

border: 'none'

230

},

231

'.cm-activeLineGutter': {

232

backgroundColor: '#49483e22'

233

}

234

}, { dark: true });

235

236

// Create custom syntax highlighting

237

const darkHighlightStyle = HighlightStyle.define([

238

{ tag: tags.keyword, color: '#f92672' },

239

{ tag: tags.string, color: '#e6db74' },

240

{ tag: tags.comment, color: '#75715e' },

241

{ tag: tags.number, color: '#ae81ff' },

242

{ tag: tags.function, color: '#a6e22e' },

243

{ tag: tags.className, color: '#a6e22e' },

244

{ tag: tags.operator, color: '#f92672' },

245

{ tag: tags.variableName, color: '#f8f8f2' },

246

{ tag: tags.typeName, color: '#66d9ef' },

247

{ tag: tags.propertyName, color: '#a6e22e' }

248

]);

249

250

// Combine into complete theme

251

const darkTheme: Extension = [

252

darkEditorTheme,

253

syntaxHighlighting(darkHighlightStyle)

254

];

255

256

// Register custom theme

257

const customTheme: IEditorTheme = {

258

name: 'monokai-dark',

259

displayName: 'Monokai Dark',

260

theme: darkTheme

261

};

262

263

registry.addTheme(customTheme);

264

```

265

266

### JupyterLab CSS Variable Integration

267

268

Using JupyterLab's CSS variables for consistent theming.

269

270

```typescript

271

// Theme using JupyterLab CSS variables

272

const jupyterLabIntegratedTheme = EditorView.theme({

273

'&': {

274

color: 'var(--jp-content-font-color1)',

275

backgroundColor: 'var(--jp-layout-color1)'

276

},

277

'.cm-content': {

278

padding: 'var(--jp-code-padding)',

279

fontFamily: 'var(--jp-code-font-family)',

280

fontSize: 'var(--jp-code-font-size)',

281

lineHeight: 'var(--jp-code-line-height)',

282

caretColor: 'var(--jp-editor-cursor-color)'

283

},

284

'.cm-focused .cm-cursor': {

285

borderLeftColor: 'var(--jp-editor-cursor-color)'

286

},

287

'.cm-focused .cm-selectionBackground, ::selection': {

288

backgroundColor: 'var(--jp-editor-selected-background)'

289

},

290

'.cm-gutters': {

291

backgroundColor: 'var(--jp-layout-color1)',

292

color: 'var(--jp-ui-font-color2)',

293

borderRight: '1px solid var(--jp-border-color2)'

294

},

295

'.cm-activeLineGutter': {

296

backgroundColor: 'var(--jp-layout-color2)'

297

},

298

'.cm-activeLine': {

299

backgroundColor: 'rgba(125, 125, 125, 0.1)'

300

},

301

'.cm-searchMatch': {

302

backgroundColor: 'var(--jp-search-selected-match-background-color)',

303

color: 'var(--jp-search-selected-match-color)'

304

},

305

'.cm-searchMatch.cm-searchMatch-selected': {

306

backgroundColor: 'var(--jp-search-selected-match-background-color)',

307

color: 'var(--jp-search-selected-match-color)'

308

}

309

});

310

311

// Syntax highlighting with CSS variables

312

const jupyterLabHighlightStyle = HighlightStyle.define([

313

{ tag: tags.keyword, color: 'var(--jp-mirror-editor-keyword-color)' },

314

{ tag: tags.string, color: 'var(--jp-mirror-editor-string-color)' },

315

{ tag: tags.comment, color: 'var(--jp-mirror-editor-comment-color)' },

316

{ tag: tags.number, color: 'var(--jp-mirror-editor-number-color)' },

317

{ tag: tags.operator, color: 'var(--jp-mirror-editor-operator-color)' },

318

{ tag: tags.punctuation, color: 'var(--jp-mirror-editor-punctuation-color)' },

319

{ tag: tags.function, color: 'var(--jp-mirror-editor-def-color)' },

320

{ tag: tags.className, color: 'var(--jp-mirror-editor-def-color)' },

321

{ tag: tags.variableName, color: 'var(--jp-mirror-editor-variable-color)' },

322

{ tag: tags.typeName, color: 'var(--jp-mirror-editor-variable-2-color)' },

323

{ tag: tags.propertyName, color: 'var(--jp-mirror-editor-property-color)' },

324

{ tag: tags.attributeName, color: 'var(--jp-mirror-editor-attribute-color)' },

325

{ tag: tags.tagName, color: 'var(--jp-mirror-editor-tag-color)' }

326

]);

327

```

328

329

### Dynamic Theme Switching

330

331

Implementing dynamic theme switching at runtime.

332

333

```typescript

334

class ThemeManager {

335

private registry: EditorThemeRegistry;

336

private currentTheme: string = 'default';

337

private editors: EditorView[] = [];

338

339

constructor(registry: EditorThemeRegistry) {

340

this.registry = registry;

341

}

342

343

// Register editor for theme updates

344

registerEditor(editor: EditorView): void {

345

this.editors.push(editor);

346

}

347

348

// Switch theme for all registered editors

349

setTheme(themeName: string): void {

350

const theme = this.registry.getTheme(themeName);

351

this.currentTheme = themeName;

352

353

this.editors.forEach(editor => {

354

editor.dispatch({

355

effects: StateEffect.reconfigure.of([

356

// Reconfigure with new theme

357

theme,

358

// ... other extensions

359

])

360

});

361

});

362

}

363

364

// Get current theme name

365

getCurrentTheme(): string {

366

return this.currentTheme;

367

}

368

369

// Get available themes

370

getAvailableThemes(): IEditorTheme[] {

371

return this.registry.themes;

372

}

373

}

374

375

// Usage

376

const themeManager = new ThemeManager(registry);

377

378

// Register editors

379

themeManager.registerEditor(editor1);

380

themeManager.registerEditor(editor2);

381

382

// Switch themes

383

themeManager.setTheme('monokai-dark');

384

themeManager.setTheme('jupyter-light');

385

```

386

387

### Responsive Theme System

388

389

Creating themes that respond to system preferences.

390

391

```typescript

392

// Responsive theme that adapts to system dark/light mode

393

const responsiveTheme = EditorView.theme({

394

'&': {

395

color: 'var(--jp-content-font-color1)',

396

backgroundColor: 'var(--jp-layout-color1)'

397

}

398

}, {

399

// Use system preference for dark mode detection

400

dark: window.matchMedia('(prefers-color-scheme: dark)').matches

401

});

402

403

// Listen for system theme changes

404

window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {

405

const newTheme = e.matches ? 'dark' : 'light';

406

themeManager.setTheme(newTheme);

407

});

408

409

// Auto-theme selection based on JupyterLab theme

410

function getAutoTheme(): string {

411

const body = document.body;

412

const isDark = body.classList.contains('jp-theme-dark') ||

413

body.classList.contains('theme-dark');

414

return isDark ? 'jupyter-dark' : 'jupyter-light';

415

}

416

```

417

418

### Advanced Theme Features

419

420

Complex theming with animations, custom properties, and conditional styling.

421

422

```typescript

423

// Theme with animations and transitions

424

const animatedTheme = EditorView.theme({

425

'.cm-cursor': {

426

transition: 'opacity 0.3s ease-in-out'

427

},

428

'.cm-selectionBackground': {

429

transition: 'background-color 0.2s ease'

430

},

431

'.cm-activeLine': {

432

transition: 'background-color 0.15s ease'

433

},

434

'.cm-focused .cm-matchingBracket': {

435

backgroundColor: 'var(--jp-brand-color0)',

436

animation: 'bracket-highlight 0.3s ease'

437

},

438

'@keyframes bracket-highlight': {

439

'0%': { backgroundColor: 'transparent' },

440

'50%': { backgroundColor: 'var(--jp-brand-color0)' },

441

'100%': { backgroundColor: 'var(--jp-brand-color0)' }

442

}

443

});

444

445

// Conditional theme based on editor state

446

const conditionalTheme = EditorView.theme({

447

'&.cm-editor.cm-readonly': {

448

backgroundColor: 'var(--jp-layout-color2)',

449

opacity: 0.8

450

},

451

'&.cm-editor.cm-has-errors': {

452

borderLeft: '3px solid var(--jp-error-color1)'

453

},

454

'&.cm-editor.cm-collaborative': {

455

borderTop: '2px solid var(--jp-brand-color2)'

456

}

457

});

458

```

459

460

## Types

461

462

```typescript { .api }

463

interface IEditorTheme {

464

readonly name: string;

465

readonly displayName?: string;

466

readonly theme: Extension;

467

}

468

469

interface IEditorThemeRegistry {

470

readonly themes: IEditorTheme[];

471

defaultTheme(): Extension;

472

addTheme(theme: IEditorTheme): void;

473

getTheme(name: string): Extension;

474

}

475

```