Lagoon icon: two teal waves on an indigo ground

VS Code colour theme

Lagoon.

A lagoon is the one place where water reads as two colours at once: luminous teal across the shallows, deep indigo where the floor drops away. That is the whole palette — monochromatic indigo chrome, one glowing teal accent, and pastel syntax that never raises its voice.

lagoon — src/contrast.ts
contrast.ts palette.js theme.js
1// Every accent has to clear 4.5:1 against the ground it sits on.
2 
3import { contrast, type Hex } from "./contrast";
4 
5const MIN_RATIO = 4.5;
6 
7export interface Swatch {
8 role: string;
9 hex: Hex;
10 locked?: boolean;
11}
12 
13export class Palette {
14 #entries = new Map<string, Swatch>();
15 
16 constructor(private readonly ground: Hex) {}
17 
18 add(role: string, hex: Hex): this {
19 if (contrast(hex, this.ground) < MIN_RATIO) {
20 throw new RangeError(`${role} fails at ${hex}`);
21 }
22 this.#entries.set(role, { role, hex, locked: false });
23 return this;
24 }
25 
26 get accents(): Swatch[] {
27 return [...this.#entries.values()].filter((s) => !s.locked);
28 }
29}
⎇ main ↑2 ↓0 ✕ 0 ⚠ 1 Ln 19, Col 34 Spaces: 2 TypeScript
Lagoon editor.background #161523

How it was built

Borrowed from what already works, then pushed one degree

The ten most-installed dark themes on the marketplace agree on more than they let on. One Dark Pro sits at #282c34, Dracula at #282a36, Tokyo Night at #1a1b26, Catppuccin at #1e1e2e, Nord at #2e3440. Every one of them is a desaturated blue between 205° and 240°. Not one uses neutral grey, and not one uses black.

They agree on the syntax map too: green strings, orange numbers, purple keywords, yellow types, blue functions. And the ones still climbing the charts — Catppuccin, Tokyo Night — are the pastel ones. Restraint is what people actually mean when they call a theme easy on the eyes.

Lagoon keeps all of that and changes one thing. Functions and every piece of UI accent are teal instead of blue, which sits split-complementary to the indigo ground — the pairing the ground was chosen for. Functions are the accent you see most often, so that single substitution is what the theme feels like.

Accents

Eight hues, each doing one job

Ratios are measured against the editor background. The build refuses to emit a theme if any accent drops below 4.5:1, or if any two accents land closer than ΔE 22 — close enough that two different roles would start to look like the same colour.

Aa teal #67e0cc 11.25:1 AAA functions, methods, decorators, every UI accent
Aa sky #82c4f3 9.56:1 AAA properties, object keys, tag attributes
Aa lavender #b49df0 7.74:1 AAA keywords, control flow, storage modifiers
Aa orchid #e891c8 7.99:1 AAA imports, language constants, regex, CSS variables
Aa rose #f08496 7.25:1 AAA errors, invalid, this / self, HTML tags
Aa apricot #f6ab79 9.45:1 AAA numbers, constants, enum members, escapes
Aa amber #f2d178 12.15:1 AAA types, classes, interfaces, namespaces
Aa green #89d193 9.96:1 AAA strings

Chrome

One hue, eleven values

Every panel, border, and background in the editor is hue 243 — an indigo with a faint plum lean. Saturation falls as lightness rises, so the light end of the ramp reads as text rather than as tinted text. Depth comes from value alone, which is why the UI has almost no visible borders.

crust#0c0c16
mantle#10101c
base#161523
elevated#1d1c2b
surface0#262537
surface1#343348
surface2#47455e
muted#817cab
subtle#9d9bbf
text#d4d4e8
bright#f0f0f9

Variants

Same design, three grounds

Soft lifts the ramp about five points of lightness for bright rooms and drops the accents to match. Dawn keeps the identical hues and inverts the value ramp — on a light ground the accents have to go dark and saturated, or pastels wash out to nothing.

lagoon — src/contrast.ts
contrast.ts palette.js theme.js
1// Every accent has to clear 4.5:1 against the ground it sits on.
2 
3import { contrast, type Hex } from "./contrast";
4 
5const MIN_RATIO = 4.5;
6 
7export interface Swatch {
8 role: string;
9 hex: Hex;
10 locked?: boolean;
11}
12 
13export class Palette {
14 #entries = new Map<string, Swatch>();
15 
16 constructor(private readonly ground: Hex) {}
⎇ main ↑2 ↓0 ✕ 0 ⚠ 1 Ln 19, Col 34 Spaces: 2 TypeScript
Lagoon Soft editor.background #222133
lagoon — src/contrast.ts
contrast.ts palette.js theme.js
1// Every accent has to clear 4.5:1 against the ground it sits on.
2 
3import { contrast, type Hex } from "./contrast";
4 
5const MIN_RATIO = 4.5;
6 
7export interface Swatch {
8 role: string;
9 hex: Hex;
10 locked?: boolean;
11}
12 
13export class Palette {
14 #entries = new Map<string, Swatch>();
15 
16 constructor(private readonly ground: Hex) {}
⎇ main ↑2 ↓0 ✕ 0 ⚠ 1 Ln 19, Col 34 Spaces: 2 TypeScript
Lagoon Dawn editor.background #f9f9fd

Role map

What gets which colour

Semantic tokens and TextMate scopes are both mapped, and mapped to agree with each other — so the colour of an identifier does not change when a language server wakes up.

TokenColourDarkDawn
Functions, methods, decorators, every UI accent teal #67e0cc #187770
Properties, object keys, tag attributes sky #82c4f3 #136eae
Keywords, control flow, storage modifiers lavender #b49df0 #5d36c9
Imports, language constants, regex, CSS variables orchid #e891c8 #a92d7c
Errors, invalid, this / self, HTML tags rose #f08496 #c32243
Numbers, constants, enum members, escapes apricot #f6ab79 #b85014
Types, classes, interfaces, namespaces amber #f2d178 #99670f
Strings green #89d193 #297a39
Commentsmuted, italic#817cab#6d6996
Punctuation, operators, parameterssubtle#9d9bbf#575676
Plain identifierstext#d4d4e8#2a2a47

Install

Three steps

  1. Open the Extensions view with ⇧⌘X (Ctrl+Shift+X on Windows and Linux) and search Lagoon. It is listed as Lagoon Color Theme, published by Lagoon. From a terminal instead:
    code --install-extension lagoon.lagoon-color-theme
  2. Open the theme picker with ⌘K ⌘T (Ctrl+K Ctrl+T).
  3. Pick Lagoon, Lagoon Soft, or Lagoon Dawn. Arrow through the list to preview each one live before committing.

Prefer to build it yourself? Grab the .vsix from the latest release, or clone the repo straight into ~/.vscode/extensions/lagoon — no packaging step required.

Follow the system, dark to light

Shipping a light variant is only useful if you never have to think about it. Point VS Code at both and it will move between them when your OS does — Lagoon after dark, Dawn in daylight.

{
  "window.autoDetectColorScheme": true,
  "workbench.preferredDarkColorTheme": "Lagoon",
  "workbench.preferredLightColorTheme": "Lagoon Dawn"
}

To pin one instead, set it outright — this is what the theme picker writes for you:

{ "workbench.colorTheme": "Lagoon" }

If you don't want the italics

Comments, control flow, parameters, and decorators are italic by design — it separates them from the tokens around them without spending another colour. If your font lacks a true italic, or you simply dislike them, override the styles rather than the theme:

{
  "editor.tokenColorCustomizations": {
    "[Lagoon]": {
      "textMateRules": [
        {
          "scope": ["comment", "keyword.control.flow", "variable.parameter"],
          "settings": { "fontStyle": "" }
        }
      ]
    }
  }
}

Changing a colour

For a one-off tweak, scope it to the variant so your other themes are left alone:

{
  "workbench.colorCustomizations": {
    "[Lagoon]": { "editor.background": "#12111d" }
  }
}

To change the palette itself, edit the HSL values in src/palette.js and rebuild. The JSON in themes/ is build output — editing it directly gets overwritten, and skips the contrast gate.

npm run build   # regenerate the three variants, the icon, and this page