Summary#
An app on the UI kit already works and reads well. What it does not have yet is a point of view: every app on the
kit's own floor looks like every other one. Osysharp.Ui.Looks is seven finished points of view to start from — each a
complete design for a business app, built and photographed over the same specimen of tables, forms, records, boards,
inboxes, dashboards, settings, dialogs and empty states, light and dark, wide and on a phone.
| look | for | what it is |
|---|---|---|
| Ledger | finance, billing, accounting, procurement | white paper and hairline rules, headings at an ordinary weight, every figure in a mono that lines up, a Prussian-navy accent |
| Relay | support desks, operations, dispatch, developer tools | the densest look; graphite that is as good dark as light, a mono for every time and id, signal orange |
| Civic | healthcare, public services, anything anyone may have to use | large type in Atkinson Hyperlegible, controls you cannot miss, square corners, bold headings, a black focus ring |
| Studio | CRM, agencies, recruiting, project tools | Figtree, ink-black actions, soft grey chrome, round corners and pill tags, colour kept for people and states |
| Signal | analytics, reporting, dashboards, growth tools | an ink rail beside a light page, figures set tall in a condensed face, cards without an outline, ultraviolet |
| Folio | content sites: guides, articles, a programme, an organisation's pages | white paper and ink, words in Source Serif 4 at 18px, headings in a heavy Epilogue, solid ink rules instead of grey boxes, a deep teal |
| Tempo | booking: classes, appointments, rooms, courts, tables | every time in Azeret Mono down a column, Albert Sans for the words, free slots as square tickets, a magenta for the time you chose |
A look is a base, not a final pick. The app derives a theme from the closest one and overrides what makes it its own — every token it does not override comes from the look:
app Hiring {
use Osysharp.Ui;
use Osysharp.Ui.Looks@0;
model "model/**/*.osy";
}
using Osysharp.Ui.Looks;
theme Brand : Looks.Studio {
Colors {
Primary = Modes.Of(light: Palette.From("#6B2D5C"), dark: Palette.From("#E7B8D9"));
OnPrimary = Modes.Of(light: "#FFFFFF", dark: "#2A0F23");
}
Radius { Tag = "6px"; }
}
[Route("/")] [AllowAnonymous]
component Home() {
render {
Stack(gap: 4, p: 6) {
PageHead("Candidates", subtitle: "12 in the pipeline");
Card("Shortlist") { Badge("Interviewing", tone: Tone.Primary); }
}
}
}Description#
Which look is closest? — osy kit looks#
osy kit looks lists every look in scope — each one's purpose, the faces it sets, its accent in light and dark, its
density and its corners — and the two lines that put one on:
$ osy kit looks
Looks — 7 finished designs for an app on the UI kit. A look is a BASE: pick the closest, derive from it, …
Looks.Ledger — white paper, hairline rules, figures in a mono that lines up, a Prussian-navy accent. …
faces Wix Madefor Text · Wix Madefor Display · DM Mono figures in DM Mono accent #1F4E89 / #86B6F2 density step 0.235rem corners 4px
…Pick by what the app is FOR, not by colour: the colour is the first thing you will change, and the density, the type and the way the page is built are what the look really decides.
Which looks are made for my kind of app? — osy kit looks <category>#
Every look names the categories of app it is a starting point for, and osy kit looks <category> lists only those.
The categories are a closed set: shop, booking, internal-tool, content-site and dashboard. A word a builder
would type for one ("dashboards", "admin", "blog", "appointments") lands on it, and any other word is refused with the
five names.
$ osy kit looks booking
Looks for booking — an app people book a time in: classes, appointments, rooms, courts, tables.
Looks.Civic — plain and certain: large type in Atkinson Hyperlegible, controls you cannot miss, square corners, …
Looks.Studio — the record desk: Figtree, ink-black actions, soft grey chrome, round corners and pill tags, …
Looks.Tempo — the timetable on the wall: times set in Azeret Mono down a column, the words in Albert Sans, …
Put one on: derive the app's theme from it: `theme Brand : Looks.X { … }`| category | its looks | how it is put on |
|---|---|---|
shop | the shop's look kits (Osysharp.Shop.Looks.*: Atelier, Drop, Rail, …) | use Osysharp.Shop.Looks.<Name>@0;, then staff pick it per version of the site (Looks — a site's whole design, picked per version) |
booking | Tempo, Civic, Studio | derive the theme |
internal-tool | Ledger, Relay, Civic, Studio | derive the theme |
content-site | Folio | derive the theme |
dashboard | Signal, Relay, Ledger | derive the theme |
Two builds of the same kind of app that start from the same look arrive at the same page, so choose by the brief,
not by the order printed, and make the look the brand's own before the first screen ([[ui-design#concept]]). A look
names its categories in its own doc comment, on a line of its own: /// Categories: booking, internal-tool.
Can I try one without editing anything? — --theme Looks.X#
Every verb that compiles takes a look's name: the app compiles wearing it, and nothing in the project changes.
$ osy compile --theme Looks.Relay # the browsable app, in Relay
$ osy test --pixels --theme Looks.Civic # every UI test photographed in CivicSo the honest way to choose is to look: photograph your own pages in two or three looks and keep the one that fits.
How do I make it my own? — derive, then override#
theme Brand : Looks.Ledger { … } is a theme that starts as everything Ledger declares. What you write inside it
replaces Ledger's value for that token — light and dark together, so an override written as one value is that value in
both modes. Change, in this order, only what makes the brand:
- The accent —
PrimaryandOnPrimary(aPalette.Fromseed for each mode), andFocusif your accent is too light to ring a control on white. - The display face —
Font { Display = "…"; }with a face you ship (osy font add), for page titles and figures' headings; keep the look's text face unless the brand has its own. - Corners —
Radius { Sm; Md; Lg; Tag; }if the brand is rounder or squarer than the look.
Leave the rest. A look's surfaces, ink steps, chrome and status colours were chosen together and contrast-checked together, and they keep working only as a set.
What does a look set? — the contract#
Every look declares the same tokens, so you can derive from any of them and override the same names:
| group | tokens |
|---|---|
Colors — the page | Bg OnBg Surface OnSurface Elevated Sunken Muted Border CardLine TextSecondary TextMuted BorderDanger Scrim |
Colors — action and state | Primary OnPrimary Focus Success Danger Warning Waiting |
Colors — the chrome (rail and bars) | Chrome OnChrome ChromeMuted ChromeFaint ChromeHover ChromeSunken ChromeElevated ChromeLine ChromeCurrent OnChromeCurrent ChromeFocus |
Font | Sans (text) · Display (titles) · Mono · Figure (statistics and number columns) |
FontSize · FontWeight | Caption Label Body Subhead Heading Title · Regular Medium Semibold Bold Headings |
Radius · Shadow | Sm Md Lg Tag · Raised Floating Deep |
Space · Length | Step (the density) · ControlSm ControlMd ControlLg Touch TouchDense |
Each look also colours the chart kit (Series1, Series2, ChartSurface, …) to match.
Does it still read after my changes? — Assert.Contrast#
A look ships with every words-on-ground pair checked at WCAG AA in light and dark. After you override its accent, run the same checks against your own theme — they read the RESOLVED tokens, so they need no browser:
[Test]
void the_brand_still_reads() {
Assert.Contrast("Colors.OnPrimary", "Colors.Primary");
Assert.Contrast("Colors.Primary.Text", "Colors.Surface");
Assert.Contrast("Colors.OnPrimary", "Colors.Primary", mode: "dark");
Assert.Contrast("Colors.Focus", "Colors.Surface", 3, mode: "dark");
}The package's own specimen carries the whole list — every pair the kit paints — in tests/contrast.test.osy; copy it
into your app's tests.
Which arrangement goes with which look?#
A look colours whatever the app is built as; it does not build it. But each look was drawn for a way of working, and
names the ARRANGEMENT it pairs with by default — osy kit looks prints it on its "arranged as" line:
| look | arrangement | navigation | how a change is kept |
|---|---|---|---|
| Ledger | the document desk — a record as tabs that go deeper, the chosen item as paper | TabbedShell (top) | SaveBar |
| Relay | the workbench — a dense table, the opened row docked right, a live log below | WorkbenchShell (split) | SaveActions |
| Civic | the flow — one plain question at a time, a check of every answer | FocusedShell (none) | "Save and continue" |
| Studio | the record desk — a list with the record beside it, an assistant a press away | SidebarShell | AutoSave |
| Signal | the command centre — a workspace of cards a person arranges | RailShell (marks) | SaveActions |
Any app may take any arrangement in any look — they are ordinary kit components. Arrangements — five ways to build a whole app, and which look wears each shows all five with a compiled example each.
See also#
- theme tokens — tokens, groups, light and dark, and deriving a theme (
#derive) - compiling with a different theme —
--theme <file or look>: dress an app in a theme it does not declare - Theme assertions — does the app really look the way the theme says? —
Assert.Token,Assert.FontandAssert.Contrast - Designing an app that looks designed — direction, type, colour, composition and motion — the design method a look is the first step of
- Arrangements — five ways to build a whole app, and which look wears each — the five arrangements, and which look pairs with each
- App shells — the shells a look colours