Skip to content

Contributing

Vinícius Campos edited this page Aug 22, 2026 · 1 revision

Contributing

The .vstheme files are generated output

FocusThemes/Themes/Focus*.vstheme are written by FocusThemes/Themes/gen-themes.py. Do not edit them by hand. A hand edit survives exactly until the next time anyone runs the generator, and it leaves no trace of why it was made.

To change a Focus colour:

  1. Edit the relevant row in the PALETTES table in gen-themes.py.
  2. Run the generator:
python FocusThemes/Themes/gen-themes.py
  1. Read the report it prints (see below).
  2. Commit both the script change and the regenerated .vstheme files.

The script is not shipped in the VSIX. It is run by hand when a palette changes, and its output is committed. GraphiteTheme/Themes/derive-light.py plays the same role for the Graphite light theme.

Reading the generator's report

Every run prints, per direction, the contrast ratio of each named role against that theme's own background, and the three closest pairs of syntax hues.

Contrast is a gate. The script exits non-zero if any role falls below its floor: 4.5:1 for everything except comment and the gutter, which are held to 3.0:1 on purpose. Those two are recessive by design — every direction makes commentary recede, and lifting them to AA would put comments at the same weight as code. The report says so on each run rather than quietly moving the floor.

Hue separation is a note, not a gate. It used to fail the run, back when the script synthesised two of the hues itself and a collision was the script's own bug. Every hue is now drawn in the design, so a tight pair is a deliberate trade-off. Two pairs are tight by construction in every direction — operator tints keyword, and variable tints the plain-text hue — so read those figures next to the contrast numbers rather than on their own. See Color Roles.

The palettes exist in two places

scripts/Generate-MarketplaceAssets.ps1 keeps a second, hand-maintained copy of every palette in its $palettes array, and draws the Marketplace screenshots from it. It must be updated whenever a palette changes, or the published screenshots will show a product that no longer exists.

Regenerate the screenshots with:

pwsh -File scripts/Generate-MarketplaceAssets.ps1

Unifying the two tables into one source is worthwhile and has not been done. Until it is, treat them as a pair.

Adding a direction

A new Focus theme is a new row in PALETTES plus a fresh GUID. The row states the three surface depths, the text and comment colours, the gutter grey, the accent, the twelve syntax hues, and the border tint. Everything else — the raised surface, the border, selection, the lifted gutter, the tag colour and the brace pair cycle — is derived from those.

You will also need to register the new .pkgdef in FocusThemes.csproj and add a row to $palettes in the asset generator.

Verifying a change

Sample/AllTokens.cs is a compact catalogue of C# classifications — all five type kinds, literals, operators, preprocessor directives, XML doc comments. Open it with the theme applied to see every token class the themes paint on one screen.

Run the test suite with:

pwsh -File scripts/Test.ps1

Building and releasing

Not repeated here, because duplicated instructions drift:

  • Build steps are in the README.
  • The release process and the one-time repository configuration it depends on are in .github/RELEASING.md.

One rule worth restating because it is easy to miss: both source.extension.vsixmanifest files must carry the same version, and the git tag must match it. The Graphite extension therefore takes a version bump even when only Focus changed.

Clone this wiki locally