Tessera Public API Guidelines
This document is the implementation policy for a C#-first Tessera API.
Audience And Layers
Tessera supports two intentional product layers:
Tessera: primary app-authoring surface for most C# developers building TUIs.Tessera.Corenamespaces: low-level runtime lane for expert/runtime-driven scenarios (inside theTesserapackage).
Advanced host seams (Tessera.Hosting) remain supported as an opt-in lane, but they are not the beginner path.
C#-First Rules
Use familiar .NET patterns by default:
- explicit object models, object initializers, and strongly typed options
EventHandler/EventHandler<TEventArgs>for control notifications- async APIs with
Asyncsuffix andCancellationTokenas the last optional parameter - immutable message payloads (records) for application message flow
IAsyncDisposablefor runtime/terminal resources
Avoid framework-specific patterns when BCL conventions already solve the problem.
Canonical Startup Pattern
Support two startup lanes:
- minimal:
await TesseraApplication.RunAsync(new App()); - configured:
TesseraApplication.CreateBuilder().UseApp<TApp>().ConfigureRuntime(...).Build()
Tessera is a library-first TUI framework. Default startup should not require DI containers or Generic Host wiring.
Minimal app shape for docs/examples:
- derive from
TesseraApp - handle app/runtime input through
Message - let built-in controls route handled input before
Update(...) - return a
ScreenfromBuild(ScreenContext)
Typical configured startup:
var app = TesseraApplication.CreateBuilder()
.UseApp<MyApp>()
.ConfigureRuntime(static runtime =>
{
runtime.MaxFps = 60;
runtime.Theme = TesseraThemes.Catppuccin(CatppuccinVariant.Mocha);
runtime.Screen = new ScreenOptions
{
AltScreen = true,
WindowTitle = "MyApp",
EnableFocusReporting = true,
};
})
.Build();
await app.RunAsync();Canonical Learning Path
Public onboarding should teach one story:
- start with overview
- complete install-and-prerequisites
- build first-app
- use the starter example order in examples:
HelloWorld->CounterForm->WorkspaceApp - use the flagship evaluation path in showcase only after the starter ladder is clear
Keep starter and flagship examples centered on the primary Tessera namespaces. Use Tessera.Core namespaces for advanced runtime-focused examples.
Canonical Theme Pattern
Theming must be semantic-token based and overrideable:
- semantic tokens (
Text,Surface,Border,State,Accent,Selection,Focus) - built-in palettes (Catppuccin, Rosé Pine)
- custom user palette
- override hierarchy: global -> control type -> control instance -> state
Focus styling must be configurable beyond "*" markers. Border/title/focus visuals should be theme-driven.
Canonical App Pattern
Default integration model for app code:
- controls raise events
- app posts domain messages with
Post(...)when state changes should flow through the state machine Update(...)applies state transitions and returns effectsBuild(...)returns the next screen
For tiny demos, direct event mutation is acceptable. Production examples should prefer message-driven updates.
Canonical Composition Pattern
Default composition path:
Screen.Build(...)+WindowBuilder- root controls from
Tessera.Controls - root layouts from
Tessera.Layout - drawing primitives only when needed:
Tessera.Components.Primitives
Alternative composition surfaces may remain public for advanced scenarios, but docs and starter examples should teach the default path first.
Preferred default imports:
using Tessera;
using Tessera.Controls;
using Tessera.Layout;Guidance Rules
- Normal app examples should prefer primary
Tesseranamespaces; advanced runtime samples can useTessera.Core.*where it adds clarity. - Public docs should use
Tessera.Styles(not legacy namespace names). - Runtime knobs for advanced hosting should live under
Tessera.Hostingdiscoverability as opt-in APIs, not the default path. - Images are V1.1 scope, not V1 scope.
Review Checklist
Before merging API/docs/example changes:
- Is the beginner path still one obvious path?
- Does this follow idiomatic C# and .NET conventions?
- Does this introduce a second equal-status integration style?
- Does this leak low-level runtime vocabulary into default app guidance?