ktsu.Semantics.Color
3.0.0
Prefix Reserved
See the version list below for details.
dotnet add package ktsu.Semantics.Color --version 3.0.0
NuGet\Install-Package ktsu.Semantics.Color -Version 3.0.0
<PackageReference Include="ktsu.Semantics.Color" Version="3.0.0" />
<PackageVersion Include="ktsu.Semantics.Color" Version="3.0.0" />
<PackageReference Include="ktsu.Semantics.Color" />
paket add ktsu.Semantics.Color --version 3.0.0
#r "nuget: ktsu.Semantics.Color, 3.0.0"
#:package ktsu.Semantics.Color@3.0.0
#addin nuget:?package=ktsu.Semantics.Color&version=3.0.0
#tool nuget:?package=ktsu.Semantics.Color&version=3.0.0
ktsu.Semantics.Color
A physically-grounded color type with linear-RGB math, perceptual Oklab operations, and built-in WCAG accessibility checks.
ktsu.Semantics.Color is one package in the ktsu.Semantics family. Start at the root README for the family overview.
Introduction
ktsu.Semantics.Color treats color the way rendering and color science do: the canonical Color type stores linear (non gamma-encoded) RGB plus straight alpha, each channel in 0..1, and all math happens in linear space. Gamma-encoded sRGB is a separate type (Srgb) that you cross into only when you convert. That distinction is the difference between correct blending, mixing, and luminance and the subtly-wrong results you get from doing arithmetic on gamma-encoded values.
On top of that foundation the package adds perceptual operations in the Oklab color space, hex and byte interop, System.Numerics vector output, and WCAG contrast tooling that can rate a color pair and automatically nudge a color until it meets a target conformance level.
Features
- Linear RGB as the hub:
Colorstores linear RGBA in 0..1, and all mixing, interpolation, and luminance math happens there. - Color spaces: sRGB (
Srgb), HSL (Hsl), HSV (Hsv), Oklab (Oklab), and Oklch (Oklch). Every color type (includingColor) converts directly to and from every other throughFrom*/To*methods. Each hop is routed through the nearest shared hub, so a conversion crosses the gamma boundary at most once and never takes a redundant gamma round-trip. - Interop: hex parse and format (
#RGB,#RRGGBB,#RRGGBBAA), 8-bit byte tuples, and linear or sRGBVector3/Vector4output (the sRGB vectors are what ImGui expects). - WCAG accessibility: relative luminance, contrast ratio (1..21), conformance rating against a background, and
AdjustForContrastwhich binary-searches Oklab lightness to hit a target while preserving hue and chroma. - Perceptual operations: Oklab distance (
DistanceTo), perceptually uniform mixing (MixOklab), and Oklab gradients (Gradient), alongside plain linearLerp. - Adjustments: lighten/darken, saturate/desaturate, hue offset, grayscale, and invert. HSL-based on
Color,Hsl, andSrgb; perceptually-uniform (lightness/chroma) variants onOklch. - Named colors: a CSS/X11 subset with case-insensitive lookup.
Installation
Package Manager Console
Install-Package ktsu.Semantics.Color
.NET CLI
dotnet add package ktsu.Semantics.Color
Package Reference
<PackageReference Include="ktsu.Semantics.Color" Version="x.y.z" />
Usage Examples
Basic Example: accessible text color
using ktsu.Semantics.Color;
Color text = Color.FromHex("#777777");
Color background = NamedColors.White;
double ratio = text.ContrastRatio(background); // ~4.48
AccessibilityLevel level = text.AccessibilityLevelAgainst(background); // Fail for AA body text
if (level < AccessibilityLevel.AA)
{
// darkens or lightens in Oklab, preserving hue and chroma, until AA is met
text = text.AdjustForContrast(background, AccessibilityLevel.AA);
}
Console.WriteLine(text.ToHex());
Perceptually uniform gradients
using ktsu.Semantics.Color;
Color start = Color.FromSrgb(0.9, 0.1, 0.2); // sRGB input, stored as linear
Color end = NamedColors.Blue;
// Oklab interpolation, inclusive of both endpoints; steps must be >= 2
IReadOnlyList<Color> ramp = start.Gradient(end, 5);
foreach (Color c in ramp)
{
(byte r, byte g, byte b, byte a) = c.ToBytes();
Console.WriteLine($"{c.ToHex()} rgba({r},{g},{b},{a})");
}
Space conversions and vector interop
using System.Numerics;
using ktsu.Semantics.Color;
Color c = NamedColors.Orange;
Hsl hsl = c.ToHsl(); // H in degrees, S/L in 0..1
Color complementary = Color.FromHsl(hsl with { H = (hsl.H + 180) % 360 });
Oklch lch = c.ToOklch(); // perceptual lightness/chroma/hue
Color brighter = Color.FromOklch(lch with { L = lch.L + 0.1 });
Vector4 imguiColor = complementary.ToSrgbVector4(); // gamma-encoded RGBA for ImGui
Conversions are not limited to Color. Any color type converts straight to any other, so you can stay in whichever space fits the task:
using ktsu.Semantics.Color;
Hsl hsl = Color.FromHex("#3366CC").ToHsl();
Oklab lab = hsl.ToOklab(); // sRGB family -> perceptual family, through linear Color
Hsv hsv = lab.ToHsv(); // and back again
Srgb srgb = Oklch.FromHsl(hsl).ToSrgb();
Color linear = hsl.ToColor(); // every satellite also has ToColor() / FromColor()
Adjusting colors
using ktsu.Semantics.Color;
Color c = Color.FromHex("#3366CC");
Color hover = c.LightenBy(0.1); // HSL lightness, alpha preserved
Color muted = c.DesaturateBy(0.3); // HSL saturation
Color accent = c.OffsetHue(30); // rotate hue 30 degrees
Color negative = c.Invert(); // per-channel negative in sRGB
// For perceptually-uniform tinting, adjust in Oklch instead of HSL
Color perceptualHover = c.ToOklch().LightenBy(0.1).ToColor(c.A);
API Reference
Color
The canonical color: linear RGBA, each channel double in 0..1. A readonly record struct with positional properties R, G, B, A.
Construction and conversion
| Name | Return Type | Description |
|---|---|---|
FromLinear(r, g, b, a = 1) |
Color |
From linear RGBA. |
FromSrgb(r, g, b, a = 1) / FromSrgb(Srgb, a = 1) |
Color |
From gamma-encoded sRGB. |
FromBytes(r, g, b, a = 255) |
Color |
From 8-bit channels. |
FromHex(string) |
Color |
Parse #RGB, #RRGGBB, or #RRGGBBAA (the # is optional). |
FromOklab / FromOklch / FromHsl / FromHsv |
Color |
From the named space. |
ToSrgb() / ToOklab() / ToOklch() / ToHsl() / ToHsv() |
space type | Convert out. |
ToHex() |
string |
#RRGGBB, or #RRGGBBAA when alpha is not full. |
ToBytes() |
(byte, byte, byte, byte) |
Rounded 8-bit RGBA. |
ToLinearVector3/4() / ToSrgbVector3/4() |
Vector3 / Vector4 |
System.Numerics interop. |
WithAlpha(a) / Clamp() |
Color |
Copy helpers. |
Operations
| Name | Return Type | Description |
|---|---|---|
RelativeLuminance |
double |
WCAG luminance. |
ContrastRatio(other) |
double |
WCAG ratio, 1..21. |
AccessibilityLevelAgainst(background, largeText = false) |
AccessibilityLevel |
Conformance rating. |
AdjustForContrast(background, target, largeText = false) |
Color |
Nudge lightness to meet a target level. |
DistanceTo(other) |
double |
Perceptual Oklab distance. |
MixOklab(other, t) |
Color |
Perceptually uniform mix (t = 0 returns this, t = 1 returns other). |
Lerp(other, t) |
Color |
Linear-RGB interpolation. |
Gradient(to, steps) |
IReadOnlyList<Color> |
Oklab gradient, inclusive of endpoints (steps >= 2). |
Adjustments
Convenience adjustments on Color operate in HSL and preserve alpha; for perceptually-uniform lightness and chroma use the Oklch equivalents via ToOklch(). All are also available natively on Hsl (returning Hsl) and Srgb (returning Srgb).
| Name | Return Type | Description |
|---|---|---|
WithSaturation(s) / SaturateBy(a) / DesaturateBy(a) / MultiplySaturation(f) |
Color |
Set or shift HSL saturation (clamped to 0..1). |
WithLightness(l) / LightenBy(a) / DarkenBy(a) / MultiplyLightness(f) |
Color |
Set or shift HSL lightness (clamped to 0..1). |
OffsetHue(degrees) |
Color |
Rotate hue around the wheel (wraps at 360). |
ToGrayscale() |
Color |
Drop saturation, keeping lightness. |
Invert() |
Color |
Per-channel negative, computed in gamma-encoded sRGB. |
Supporting types
| Type | Description | Base conversion |
|---|---|---|
Srgb |
Gamma-encoded sRGB, the only gamma-boundary crossing. | ToLinear() / FromLinear(Color) (also as ToColor() / FromColor()) |
Hsl / Hsv |
Hue in degrees, saturation and lightness/value in 0..1, defined over sRGB. | FromSrgb / ToSrgb |
Oklab |
Perceptual color space (Ottosson 2020). | FromColor / ToColor; polar via ToOklch / FromOklch |
Oklch |
Polar form of Oklab. | ToOklab / FromOklab |
AccessibilityLevel |
enum: Fail = 0, AA = 1, AAA = 2. |
— |
NamedColors |
Common colors (Black, White, Red, Orange, Transparent, ...), plus All and TryGet(name, out color) with case-insensitive keys. |
— |
The satellite spaces carry their own adjustments: Hsl and Srgb have the full HSL set (saturation/lightness/hue/grayscale; Srgb routes these through HSL and also adds Invert()), while Oklch exposes perceptually-uniform WithLightness/LightenBy/DarkenBy, chroma ops (WithChroma/MultiplyChroma/SaturateBy/DesaturateBy/ToGrayscale), and OffsetHue. Color's adjustment methods forward to Hsl.
Converting between spaces
Beyond the base conversion listed above, every color type (including Color) exposes To{Space}() and From{Space}(...) for every other space, plus a uniform ToColor() / FromColor(Color) pair. Those extra methods are one-liners that reuse the base conversions rather than reimplementing any color math, routed through the nearest shared hub:
- Within the sRGB family (
Srgb,Hsl,Hsv) conversions route throughSrgb, so no gamma decode/encode happens. - Within the perceptual family (
Oklab,Oklch) conversions route throughOklab. - Across the two families conversions route through the linear
Colorhub, crossing the gamma boundary exactly once.
So srgb.ToHsl() is the direct HSL definition, while hsl.ToOklab() goes Hsl → Srgb → linear Color → Oklab. Because the satellite spaces carry no alpha, cross-space conversions preserve only the color; use the Color overloads (or WithAlpha) when you need alpha.
FromHex throws ArgumentException for lengths other than 3, 6, or 8, and Gradient throws ArgumentException for steps < 2.
Contributing
Contributions are welcome! Feel free to open issues or submit pull requests.
License
This project is licensed under the MIT License. See the LICENSE.md file for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 is compatible. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- System.Memory (>= 4.6.3)
- System.Numerics.Vectors (>= 4.6.1)
- System.Threading.Tasks.Extensions (>= 4.6.3)
-
.NETStandard 2.1
- No dependencies.
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages (6)
Showing the top 5 NuGet packages that depend on ktsu.Semantics.Color:
| Package | Downloads |
|---|---|
|
ktsu.ThemeProvider
A semantic color theming library for .NET applications that provides 44+ beautiful themes with intelligent color mapping, framework integration, and accessibility-first design. Features include theme discovery through a centralized registry, semantic color specifications (meaning + priority instead of hardcoded colors), built-in Dear ImGui support, and advanced color science with perceptually uniform color spaces. |
|
|
ktsu.ThemeProvider.ImGui
A semantic color theming library for .NET applications that provides 44+ beautiful themes with intelligent color mapping, framework integration, and accessibility-first design. Features include theme discovery through a centralized registry, semantic color specifications (meaning + priority instead of hardcoded colors), built-in Dear ImGui support, and advanced color science with perceptually uniform color spaces. |
|
|
ktsu.ImGui.Styler
A powerful styling library for ImGui.NET interfaces featuring 50+ built-in themes (Catppuccin, Tokyo Night, Gruvbox, Dracula, Nord, and more), interactive theme browser, scoped styling system for colors and style variables, advanced color manipulation with hex support and accessibility features, automatic content alignment and centering, semantic text colors, button alignment, and indentation utilities. |
|
|
ktsu.ImGui.Widgets
A comprehensive library of custom widgets and UI components for ImGui.NET, featuring radial progress bars with countdown/count-up timers, tabbed interfaces with drag-and-drop support, type-safe combo boxes, resizable divider containers, powerful search boxes with fuzzy matching, icons with event handling, flexible grid layouts, and scoped utilities for IDs and disabling elements. |
|
|
ktsu.ImGui.Color
A comprehensive .NET library suite for building desktop applications with Dear ImGui. Provides application scaffolding with PID-controlled frame limiting, custom widgets (TabPanel, SearchBox, Knob, RadialProgressBar, DividerContainer, Grid), modal dialogs (file browser, input prompts, searchable lists), a theming system with 50+ built-in themes and scoped styling, and an attribute-based node graph editor with physics-based layout. Built on Hexa.NET.ImGui bindings and Silk.NET for cross-platform windowing. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.2.0 | 41 | 8/27/2026 |
| 3.1.4 | 274 | 8/25/2026 |
| 3.1.3 | 489 | 8/24/2026 |
| 3.1.2 | 671 | 8/21/2026 |
| 3.1.1 | 836 | 8/19/2026 |
| 3.1.0 | 287 | 8/19/2026 |
| 3.0.1 | 317 | 8/18/2026 |
| 3.0.0 | 866 | 8/15/2026 |
| 2.9.14 | 764 | 8/14/2026 |
| 2.9.13 | 103 | 8/14/2026 |
| 2.9.12 | 90 | 8/14/2026 |
| 2.9.11 | 248 | 8/14/2026 |
| 2.9.10 | 87 | 8/14/2026 |
| 2.9.9 | 87 | 8/14/2026 |
| 2.9.8 | 86 | 8/14/2026 |
| 2.9.7 | 100 | 8/14/2026 |
| 2.9.6 | 97 | 8/14/2026 |
| 2.9.5 | 90 | 8/14/2026 |
| 2.9.4 | 99 | 8/14/2026 |
| 2.9.3 | 677 | 8/7/2026 |
## v3.0.0 (major)
Changes since v2.0.0:
- [major] Sonar: clear all 24 open issues and the one security hotspot ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] MSTEST0058/0061: fix the two MSTest analyzer diagnostics ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] S4136/S4144: group overloads and drop duplicate private factories ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] S3776: reduce cognitive complexity in seven methods ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] S3267: clear the last two generator loops ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] S1172: drop the last four unused generator parameters ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] S3267: simplify two generator loops with LINQ ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] S1192: name the literals the generators emit ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Add a local SonarCloud check; simplify AdjustForContrast; test PatternValidationRule ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Fix a sentence-case bug, harden the pattern regex, tidy the generator ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Extract the shared delimiter-separated casing rules ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Adopt ktsu.Sdk 2.27.0: KTSU0001 package references ([@matt-edmondson](https://github.com/matt-edmondson))
- Merge remote-tracking branch 'origin/main' into chore/sonarcloud-cleanup-2 ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Cover the untested casing and relative-path branches ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Cover PropertyTemplate shorthand; drop the duplicated const docs ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] S4136: group AsAbsolute overloads in the relative path types ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Fix SonarCloud issues: S2223, S1192, S6610, S6580, S3358 ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Fold the generator tests into Semantics.Test so their coverage is collected ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Add source generator tests to cover the generator pipeline ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] MSTEST0037: use the intent-revealing assertions ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] MSTEST0068: CollectionAssert.AreEqual -> Assert.AreSequenceEqual ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Migrate file headers to the one-line ktsu-dev form ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Fix SonarCloud BLOCKER issues in test suite ([@matt-edmondson](https://github.com/matt-edmondson))
- Sync .serena\.gitignore ([@KtsuTools](https://github.com/KtsuTools))
- Sync .runsettings ([@KtsuTools](https://github.com/KtsuTools))
- Sync .editorconfig ([@KtsuTools](https://github.com/KtsuTools))
- Sync .gitattributes ([@KtsuTools](https://github.com/KtsuTools))
- Sync global.json ([@KtsuTools](https://github.com/KtsuTools))
- [minor] Return the root itself from AbsoluteDirectoryPath.Parent ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Fix directory detection in SemanticRelativePath.Make ([@matt-edmondson](https://github.com/matt-edmondson))
- Make the new path tests platform-agnostic ([@matt-edmondson](https://github.com/matt-edmondson))
- Emit CRLF from the generators and re-enable package validation ([@matt-edmondson](https://github.com/matt-edmondson))
- [minor] Compare semantic strings by value and expose path values on interfaces ([@matt-edmondson](https://github.com/matt-edmondson))
- [minor] Add color adjustment operations across color spaces ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Fix Oklch assertion in color cross-conversion tests ([@matt-edmondson](https://github.com/matt-edmondson))
- [minor] Add cross-space conversions between all color types ([@matt-edmondson](https://github.com/matt-edmondson))
- fix(music): use ValueTuple.GetHashCode instead of System.HashCode for netstandard2.0 compatibility ([@matt-edmondson](https://github.com/matt-edmondson))
- refactor(music): split Progression.TryParse into helpers to cut cognitive complexity and remove always-true check (SonarQube S3776/S2583) ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(music): update examples and references for Parse/TryParse rename and chart-style progressions ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): chart-style Arrangement ToString + Parse/TryParse + structural equality ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): chart-style Section ToString + Parse/TryParse ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music)!: replace bar-delimited Progression.Parse with chart-style ToString/Parse/TryParse + structural equality; migrate call sites ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): rename Form.FromPattern to Parse/TryParse, canonical ToString, structural equality ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): Rest canonical ToString + Parse/TryParse ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): Note canonical ToString + Parse/TryParse ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): ChordEvent canonical ToString + Parse/TryParse ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): canonical Chord ToString, TryParse, ParseRoot via Notation ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): Key canonical ToString + Parse/TryParse; roman-numeral accidental via Notation ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): Scale canonical ToString + Parse/TryParse ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): Tempo canonical ToString + Parse/TryParse ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): Velocity canonical ToString + Parse/TryParse ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): TimeSignature canonical ToString + Parse/TryParse ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): Duration canonical ToString + Parse/TryParse ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): Interval canonical ToString + Parse/TryParse ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): rename Mode.FromName to Parse/TryParse, canonical ToString ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): typed Pitch factory, rename FromName to Parse/TryParse, canonical ToString ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): typed PitchClass factory, Parse/TryParse, canonical ToString ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add NoteLetter and Accidental enums ([@matt-edmondson](https://github.com/matt-edmondson))
- docs: implementation plan for music type-safe factories and canonical round-trip ToString ([@matt-edmondson](https://github.com/matt-edmondson))
- docs: revise music factories spec with canonical round-trip ToString and chart-style aggregate format ([@matt-edmondson](https://github.com/matt-edmondson))
- docs: design spec for music type-safe factories and Parse/TryParse convention ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] docs: add per-package READMEs and turn root README into a family index ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(music): document the analysis aggregate layer ([@matt-edmondson](https://github.com/matt-edmondson))
- test(music): lock guard/coverage contracts; dedupe chromatic scale check ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Form pattern extraction and named-form recognition ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Arrangement container ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Section structural unit ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add chromatic chord identification ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add key inference by diatonic fit ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(music): switch key inference to quality-weighted scoring ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add cadence detection ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add roman-numeral labeling and functional classification ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Progression.Parse bar-delimited chord syntax ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Progression core (construction, totals, empty rejection) ([@matt-edmondson](https://github.com/matt-edmondson))
- fix(music): drop CA1859 pragma; use IMusicalEvent helper in ChordEvent test ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add ChordEvent harmonic event type ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(music): implementation plan for analysis aggregate layer ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(music): design spec for analysis aggregate layer ([@matt-edmondson](https://github.com/matt-edmondson))
- test(strings): add As<T> round-trip test for Uuid ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(strings): reconcile spec As<T> test bullet with implemented roster ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(strings): document Identifiers package in README ([@matt-edmondson](https://github.com/matt-edmondson))
- chore(strings): finalize Identifiers package and document it ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(strings): add JwtToken identifier type ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(strings): add Iban identifier type ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(strings): add Isbn identifier type ([@matt-edmondson](https://github.com/matt-edmondson))
- style(strings): add trailing newline to IsCreditCardNumberAttribute.cs ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(strings): use pattern-matching form in Tasks 5-6 (IDE0078) ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(strings): add CreditCardNumber identifier type ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(strings): add Ulid identifier type ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(strings): align plan Tasks 3-7 with repo conventions (ThrowsExactly, no using System, Ensure.NotNull) ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(strings): add Uuid identifier type ([@matt-edmondson](https://github.com/matt-edmondson))
- chore(strings): scaffold Semantics.Strings.Identifiers package ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(strings): correct empty-string handling in spec; add implementation plan ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(strings): spec for Semantics.Strings.Identifiers (Phase 0) ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(color): correct Oklab round-trip tolerance note in plan ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(color): add NamedColors and gamma-regression tests ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(color): add Oklab mix, lerp, distance, and gradient ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(color): add WCAG luminance, contrast, and accessibility adjustment ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(color): add HSL and HSV conversions ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(color): add Oklab and Oklch perceptual spaces ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(color): add hex and byte conversions (sRGB-interpreted) ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(color): add Srgb space and gamma-correct sRGB<->linear boundary ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(color): add semantic-domains roadmap, Semantics.Color spec and plan ([@matt-edmondson](https://github.com/matt-edmondson))
- style(color): strip UTF-8 BOM and add final newline (editorconfig) ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(color): scaffold Semantics.Color with canonical linear Color type ([@matt-edmondson](https://github.com/matt-edmondson))
- docs: cover Semantics.Music score primitives, frequency, inversions, roman parsing ([@matt-edmondson](https://github.com/matt-edmondson))
- [minor] feat(music): score primitives, frequency bridge, inversions/transpose, roman-numeral parsing ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): parse roman numerals back into chords (inverse of RomanNumeralOf) ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add chord inversions and Transpose on Chord/Scale/Key ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Pitch<->frequency (A440) and interval cents ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add score primitives (Velocity, Tempo, Note, Rest) with real-time conversion ([@matt-edmondson](https://github.com/matt-edmondson))
- Merge feature/semantics-music-types: musical value types ([@matt-edmondson](https://github.com/matt-edmondson))
- [minor] feat(music): musical value types (pitch, interval, scale, chord, key, duration) ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Key with roman-numeral function; spell chromatic degrees conventionally (flat-preference) ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Chord engine with parsing, tones, and voicing (full HeatDeathRomance vocabulary) ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add TimeSignature with bar and beat durations ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add rational Duration with arithmetic and dotted/tuplet support ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Scale and ScaleDegree with degree resolution ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Mode with full standard scale catalog (diatonic, jazz, symmetric, pentatonic) ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Interval with octave folding and pitch difference ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Pitch with MIDI/name conversion and transpose ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): scaffold Semantics.Music with PitchClass ([@matt-edmondson](https://github.com/matt-edmondson))
- fix(packaging): unblock the 2.0 release pipeline [patch] ([@matt-edmondson](https://github.com/matt-edmondson))