Theme Font Weight Gallery feed (extension)
Set independent code editor font weights for light and dark Visual Studio themes. Written entirely in F#.
▼ Download- Author
- Jakub Majocha
- Version
- 0.2.13
- Updated
- 7 minutes ago
- Tags
- font;font weight;editor;theme;dark theme;light theme;F#
- Supports
- VS 2026
Theme Font Weight
The default Visual Studio editor font is too heavy, especially in dark themes. This extension allows to make the editor text lighter. You set one weight for light themes and one for dark themes. Written entirely in F#.
You need a variable weight font like JetBrains Mono.
Go to Tools > Options > Environment > Theme Font Weight. Changes apply immediately. The settings sync to your other Visual Studio installations.
The weight is a shift from Normal (400). For example, Light (300) changes regular text to 300 and bold text to 600, so bold text stays bold. The extension does not change the font family, size or colors.
Build and install
Requires Windows and the .NET 11 SDK. Targets .NET Framework 4.8 (the in-process VS host) and Visual Studio 2026, including Insiders, on x64. The current public VS SDK and VSSDK 18 build tools generate the VSIX and package registration directly from the F# assembly. No C# bridge or source generator is used.
dotnet build .\ThemeFontWeight.sln -c Release
dotnet run --project .\tests\ThemeFontWeight.Tests -c Release
.\tests\ThemeFontWeight.Formatting.Tests\bin\Release\net48\ThemeFontWeight.Formatting.Tests.exe
Versions are Major.Minor.Commits: VersionMajorMinor in Directory.Build.props (set by hand), then the number of commits to HEAD (Directory.Build.targets). The VSIX and the file versions use it; assembly versions stay at Major.Minor.0.0. Pass -p:GitHeight=<n> to set the last part.
Install src\ThemeFontWeight\bin\Release\net48\ThemeFontWeight.vsix using the VSIX installer, then restart Visual Studio. Uninstall through Extensions > Manage Extensions to return to Visual Studio's normal font-weight behavior. Select your Insiders installation in the installer if you have multiple VS instances.
Publish
The GitHub workflow .github/workflows/build.yml builds and tests each push and pull request. Each workflow run keeps the VSIX as an artifact.
- Open VSIX Gallery: each push to
mainuploads the VSIX to the Open VSIX Gallery. This gives nightly builds. No account or token is necessary. - Visual Studio Marketplace: put
[release]in the commit message, or start the workflow manually. The workflow then publishes to the Marketplace and makes a GitHub release with the tagv<version>. The repository secretVS_MARKETPLACE_PATmust contain the token (see below).
To get the nightly builds in Visual Studio:
- Select Tools > Options > Environment > Extensions.
- Add an additional extension gallery with this URL:
https://www.vsixgallery.com/feed/extension/ThemeFontWeight.89a14773-bca2-4e64-bf7e-042c93a72c09 - Find the extension in Extensions > Manage Extensions in that gallery.
To publish to the Visual Studio Marketplace from your computer, commit all changes first. Then run:
$env:VS_MARKETPLACE_PAT = '<token>'
.\Publish-Vsix.ps1
The token is an Azure DevOps personal access token with the Marketplace (Manage) scope and All accessible organizations. The listing is in src\ThemeFontWeight\publishManifest.json, and the Marketplace page is overview.md in the same folder. The Marketplace does not accept a version again, so each upload needs a new commit.
Experimental instance and debugging
Open ThemeFontWeight.sln in VS 2026 Insiders, choose the extension project as the startup project, and press F5. Builds inside VS deploy to the Exp profile and launch devenv.exe /RootSuffix Exp /Log. Command-line dotnet build produces a VSIX without installing it.
Alternatively, close the experimental instance and run:
.\Deploy-Experimental.ps1 -Launch
The script discovers VS 2026 installations including Insiders, uses the native VSIX installer to install into Exp, then runs /UpdateConfiguration to merge package and Unified Settings registrations. It leaves normal VS profiles untouched and refuses to deploy while Exp is running. F5 uses this same installation path after building, rather than SDK raw-copy deployment, which does not reliably register a new extension in the VS 2026 extension cache. Installing a VSIX into your normal profile does not install it into Exp, and launching Exp alone does not deploy an extension.
Implementation
registration.json declares a searchable Unified Settings category, validated enum values, defaults, labels, and descriptions. ProvideSettingsManifest on an F# AsyncPackage generates the manifest registration automatically at build time. The settings UI does not require loading the package or opening a document. Each setting declares migration.roamingOnly to connect Unified Settings to Visual Studio's roaming store without introducing a legacy settings location or a separate extension-managed store.
An F# MEF IWpfTextViewCreationListener acquires the native Unified Settings service asynchronously. It reads effective values through ISettingsReader and subscribes to live setting changes. There is no synchronous LoadPackage call, duplicated settings store, or custom mutable settings singleton.
In-process IClassificationFormatMap remains necessary for changing the WPF editor's typefaces. Default and classification-specific weights are shifted from their original values: VS's IsBold flags force weight 400 or 700 and would otherwise discard relative weights during the formatting merge. Those flags are represented by explicitly weighted typefaces without changing italic flags or Baselines are captured once per format map, shared by all views using it, and retain the original bold distinction. They are refreshed when VS or another extension changes formatting. All changes are batched. Per-view maps update changes, with UI-thread marshaling and subscription cleanup when views close. Settings retrieval errors go to the VS Activity Log without replacing them with success-shaped defaults.
The public ISettingsManager API documents the Unified Settings service, but its marker type is only in VS's internal interop assembly. A GUID-only local service marker requests that service without referencing internal assemblies.
API references
- Unified Settings registration (
ProvideSettingsManifest) - Native Unified Settings manager
- Effective values and change subscriptions (
ISettingsReader) - Production Unified Settings registration in Roslyn/Razor
- VSSDK-compatible modern extensibility and in-process APIs
License
MIT.