# Building a look — from an empty kit to a design a store can wear

> A look is a kit that draws two contracts: the general site contract (`Osysharp.Site.Looks` — a hero, a quote, the footer) and one category's (`Osysharp.Shop.Looks` for a shop — a row of products, the store's frame and pages). This page builds one from an empty folder: the manifest, the `look` and its theme, a design of each part and block type, its fonts, a block of its own, and seeing it in a store with `osy test --pixels`.

<!-- id: ui-building-a-look · area: ui · stability: stable · html: https://osysharp.com/reference/ui/building-a-look/ -->

## Summary        {#summary}
A **look** is a site's whole design, shipped as a kit and picked per site version at the page desk. It sits on two
contracts and draws both:

| layer | package | what it says |
|---|---|---|
| **a look** | yours, in your own namespace — `MyStore.Looks.Linen` | how everything looks: the frame, the pages, the theme and fonts, a design of each block type, and blocks of its own |
| **the category contract** | `Osysharp.Shop.Looks`, inside the shop kit | what a SHOP has: a row of products, the shelves as tiles, and the parts of a store — its frame, front page, listing, grid, a piece's page, a content column, a "not here" |
| **the site contract** | `Osysharp.Site.Looks` | what ANY site has: a hero, a picture with words, a quote, a sign-up, a banner, a gallery, the strip across the top, the footer's words, links, opening hours and address |

A contract is an `abstract component` — parameters and no body. A look's design writes `: Hero` and takes exactly the
contract's parameters, checked like a class against an interface. What staff arrange is stored as the TYPE and its
values, so a store switching from one look to another keeps every word and photograph and draws them in the new design.

**The worked example is the shop template's own ten looks** (`kits/look-atelier` … `kits/look-loud`): the five designs
of the Vall fashion store and the five of the Second Round thrift store, each a complete look kit drawing both contracts.
This page builds a small one of your own beside them — **Linen**: undyed linen, ink-blue words, one rust accent — in your
own namespace, from an empty folder. Every one of the shop's looks has the same shape, so read any of them beside it.

## Signature      {#signature}
```osy syntax
package MyStore.Looks.Linen { … use Osysharp.Shop@1; use Osysharp.Site.Looks@0; use Osysharp.PageBuilder@1; }

namespace MyStore.Looks.Linen;
look Linen { Label = "Linen"; Hint = "…"; Theme = LinenTheme; }
theme LinenTheme { Colors { … } Font { … } FontSize { … } Radius { … } }

component LinenFrame() : ShopFrame { … }                    // one design per part of a store
component LinenHero(<exactly Hero's parameters>) : Hero { … } // one design per block type you draw your way
[Block("A note from the weaver")] component LinenNote(…) { … } // a block only this look has
```

## Description    {#description}

### What goes in the kit's manifest? {#manifest}
A look kit is a folder with a `package.osy`, its source under `model/`, its fonts under `fonts/` and its own
`osyrin.lock`. The manifest names the packages its designs read: the two contracts, the PageBuilder kit (the menu and the
look switch every frame carries), and what draws photographs.

```osy syntax
// looks/linen/package.osy — a LOOK for a shop. It draws Osysharp.Site.Looks and Osysharp.Shop.Looks and
// carries no data and no logic of its own.
package MyStore.Looks.Linen {
  version     "0.1.0";
  minPlatform "0.9.0";
  summary     "a shop look — undyed linen, ink-blue words, one rust accent.";
  model "model/**/*.osy";
  use Osysharp.Ui;
  use Osysharp.Blocks;
  use Osysharp.Storage;
  use Osysharp.Shop@1;
  use Osysharp.Site.Looks@0;
  use Osysharp.PageBuilder@1;
}
```

The summary says what the look is FOR — "a shop look" — because a look draws one category's contract. Run `osy lock` in
the folder to pin those packages.

A look kit carries **no data and no logic**, and asks for no egress and no secret. Its designs read what a store's page
reads, as the visitor — so a design written by someone else cannot send a customer's data anywhere.

### How does a store wear it? {#store}
The store does not change shape for a look. Its pages keep what is the STORE's — the data they read, the checkout's
steps — and hand the look what it draws: its layout places `ShopFrame` once, its front page is `FrontPage()`, a piece's
page reads the piece and hands it to `ProductPage`. `app.Look` names the look the site wears until staff choose another.

```osy title="a store that wears Linen" test app=ui-building-a-look
app Linen {
  use Osysharp.Ui;
  use Osysharp.Storage;
  use Osysharp.Blocks;
  use Osysharp.Shop@1 { egress "api.resend.com"; secret "ResendApiKey"; }
  use Osysharp.PageBuilder@1;
  use Osysharp.Site.Looks@0;
  model "model/**/*.osy";
}

using Osysharp.Shop;
using Osysharp.Shop.Looks;

app.Look = Linen;

[Layout]
[AllowAnonymous]
component Shop() { render { ShopFrame { Outlet(retain: 8); } } }

[Route("/")]
[Layout(Shop)]
[AllowAnonymous]
component Home() { render { FrontPage(); } }

[Route("/shop")]
[Layout(Shop)]
[AllowAnonymous]
component Catalogue() { render { ShopSearchPage(); } }

[Route("/p/{slug}")]
[Layout(Shop)]
[AllowAnonymous]
component Piece(string slug) {
  live var product = Product.Include(p => p.Images).Include(p => p.Variants).SingleOrDefault(p => p.Slug == slug);
  render { ProductPage(product, note: "Free returns within 30 days."); }
}

[Route("/account")]
[Layout(Shop)]
component AccountPage() { render { ShopSection { ShopAccountPanel(); } } }
```

In a real kit, the store installs it with one line in its manifest — `use MyStore.Looks.Linen@0;` — and `osy lock`. A store
may install several; each appears in the look picker on the desk (`/staff/pages`), and staff pick one per version of the
site: drafted, previewed as a whole site, switched on a date ([[ui-pages-kit#looks]]).

### Which look is it, and what does it wear? {#look}
The `look` names itself, and its theme. Put it in a namespace of its own — everything that namespace declares is the
look's: its designs, its regions, its own blocks.

```osy title="the look and its theme" test app=ui-building-a-look
namespace MyStore.Looks.Linen;

/// Undyed linen, ink-blue words, one rust accent for the thing to press.
look Linen {
  Label = "Linen";
  Theme = LinenTheme;
}

theme LinenTheme {
  Colors {
    Bg            = "#F6F2EA";
    OnBg          = "#1D2A3A";
    Surface       = "#EDE6DA";
    OnSurface     = "#1D2A3A";
    Border        = "#D9D0C1";
    TextMuted     = "#6B6A66";
    TextSecondary = "#3E4652";
    Primary       = Palette.From("#A2472B");
    OnPrimary     = "#FFFFFF";
    OnImage       = "#FFFFFF";
  }
  Font {
    Display = "\"Fraunces\", Georgia, serif";
    Sans    = "\"Hanken Grotesk\", system-ui, sans-serif";
  }
  FontSize { Mega = "clamp(48px, 7vw, 112px)"; Title = "26px"; Body = "15px"; }
  Radius { Card = "6px"; Md = "6px"; }
}
```

`Label` is what staff see in the picker; `Hint`, the line under it, defaults to the first sentence of the doc comment.
The theme is served only while the site wears this look, under `:root[data-look="linen"]`, so several looks' palettes
live in one store without taking each other's names. It re-skins EVERYTHING on the page while the look is worn — the
store's own pages (the bag, the checkout) and any design that is not the look's. Name the tokens a store's pages read
(`Bg`, `OnBg`, `Surface`, `Primary`, `TextMuted`, …) so those pages fit too ([theme tokens](https://osysharp.com/reference/ui/theming/)).

### Which parts must a shop look draw? {#parts}
The shop contract's PARTS are the store's frame and pages. They have no plain design of their own, so **a shop look
draws all seven**. A part the worn look skips is drawn by the design the store's DEFAULT look (`app.Look`) gives it — or,
when that look skips it too, the first installed look that draws it — in the worn look's tokens. That keeps a store
whole, but it is somebody else's frame or page in your colours, so a finished look draws its own.

| part | what it is handed |
|---|---|
| `ShopFrame()` | the page, as its slot — draw the header, the menu, the strip across the top, the footer, the account and the bag around it |
| `FrontPage()` | nothing — it is a region staff arrange |
| `ShopSection()` | content, as its slot — the look's column: margins and width |
| `ListingPage(title, lead, products, shelves, current)` | a shelf, or everything; the store's own `slot empty { … }` for an empty one |
| `ProductGrid(products)` | the pieces to lay out |
| `ProductPage(product, note)` | the piece with its images, variants, specs and shelf read, or null; the store's line under it |
| `NotFound(message, linkLabel, to)` | what is not here, and where to go instead |

```osy title="the frame, the column and the not-found" test app=ui-building-a-look
namespace MyStore.Looks.Linen;

using Osysharp.Ui;
using Osysharp.Blocks;
using Osysharp.PageBuilder;
using Osysharp.Shop;
using Osysharp.Shop.Looks;
using Osysharp.Site.Looks;

/// The frame around every page: the menu, the strip across the top and the footer — each staff's to arrange.
[Composable]
component LinenFrame() : ShopFrame {
  live var menu = SiteMenu("menu", [ new NavItem { Label = "Shop", To = "/", Exact = true } ]);
  live var chrome = new AppChrome { Product = "Linen", Home = "/", Nav = menu };
  render {
    EditorialShell(chrome) {
      SiteLook();
      ShopVisitSync();
      Slot;
      slot promo {
        Region("announcement", allow: [Announcement]) { Announcement(words: "Free delivery over 800 kr"); }
      }
      slot actions { ShopAccountButton(); }
      slot footer {
        EditorialFooter(chrome, note: "Linen") {
          slot about {
            Region("footer", allow: [FooterWords, FooterLink, OpeningHours, Address, FooterSignUp]) {
              FooterWords(words: "Sheets, towels and table linen, woven slowly.");
            }
          }
        }
      }
    }
  }
}

/// A page's content column.
component LinenSection() : ShopSection {
  variants { base { W = "100%"; MaxW = "1200px"; Mx = "auto"; Px = 5; } }
  render { Box { Slot; } }
}

/// Something that is not here, and where to go instead.
component LinenNotFound(string message, string linkLabel = "", string to = "") : NotFound {
  render {
    LinenSection {
      Stack(gap: 3, py: 16) {
        Text(message, fontFamily: Font.Display, fontSize: FontSize.Title);
        if (linkLabel != "" && to != "") { Link(href: to, label: linkLabel) { Text(linkLabel + " →"); } }
      }
    }
  }
}
```

Four calls in a frame are what make it a frame staff can run:

- **`SiteLook()`** keeps the browser in the look the version wears, and moves it when staff switch or preview another.
- **`SiteMenu("menu", […])`** is the menu staff edit at the desk; the list you hand it is what it shows until they do.
- **`Region("announcement", …)`** and **`Region("footer", …)`** are the strip and the footer staff arrange.
- **`ShopVisitSync()`** keeps the visitor's bag in step with the store.

⚑ **Use the region and menu names every look uses** — `"menu"`, `"announcement"`, `"footer"`, `"front"`. A region is
keyed by its name, so a store switching from Atelier to Linen keeps the strip, the menu and the footer staff arranged.
A region only your look names stays in the arrangement and waits; the desk lists it before a switch.

### How does it draw the blocks staff arrange? {#blocks}
The front page is a `Region` of blocks. What you write inside it is how it stands until staff arrange it. Draw each
block type your way by designing its contract — `LinenHero(…) : Hero` — with the contract's parameters in the same
names, types and order. A design that differs is refused, naming the difference (*"'LinenHero' implements Hero, and
leaves out its parameter 'ground'"*).

```osy title="the front page, Linen's hero and a block only Linen has" test app=ui-building-a-look
namespace MyStore.Looks.Linen;

using Osysharp.Ui;
using Osysharp.Blocks;
using Osysharp.Shop.Looks;
using Osysharp.Site.Looks;

/// The front page: a region staff arrange. What is written inside it is how it stands until they do.
[AllowAnonymous]
component LinenFront() : FrontPage {
  render {
    Region("front", allow: [Hero, ProductRow, CategoryTiles, Quote, LinenNote]) {
      Hero(title: "Woven slowly.", words: "Linen for the bed and the table.", button: "Shop the linen", link: "/shop");
      ProductRow(title: "New in", count: 4);
      LinenNote();
    }
  }
}

/// Linen's design of the general Hero: exactly the contract's parameters, drawn its own way.
[Composable]
component LinenHero(string title, Markdown words = "", Picture photo = "", Picture second = "", string button = "Shop now",
                    PageLink link = "/", Color ground = Colors.Surface) : Hero {
  render {
    Box(bg: ground, color: Colors.On(ground, Colors.OnSurface), label: "Hero: " + title) {
      Stack(gap: 4, px: 5, py: 16, maxW: "1200px", mx: "auto") {
        Text(title, fontFamily: Font.Display, fontSize: FontSize.Mega, lineHeight: "1");
        if (words != "") { Markdown(words); }
        if (button != "") { Row { Link(href: link, label: button) { Text(button + " →", color: Colors.Primary); } } }
      }
    }
  }
}

/// A note from the weaver — a block only Linen has.
[Block("A note from the weaver")]
component LinenNote([Label("The note"), MaxLength(200)] string words = "Every sheet is washed once before it leaves us.") {
  render { Text(words, fontFamily: Font.Display, fontStyle: FontStyle.Italic, px: 5, py: 10, textAlign: TextAlign.Center); }
}
```

Three things to keep:

- **Paint a ground staff chose with its ink.** A contract with a `Color ground` lets staff pick the ground;
  `Colors.On(ground, …)` sets the words in the ink the theme pairs with it, so a dark ground never gets dark words.
- **A hero with no photograph is still a hero.** Staff may arrange a hero of words in one look and switch to yours;
  draw that case, not an empty frame waiting for a picture.
- **A block of your own** is any `[Block]` component in the look's namespace. Staff place it like any other, and it is
  drawn only while the site wears your look: switching away, the desk lists it as not coming along, and it comes back
  with your look.

### How does it draw a listing and a piece? {#pages}
The listing, the grid and a piece's page are handed the store's rows; the look decides how they stand. The shop's helpers
for a look — `LookPrice`, `LookSecondCover`, `LookSizeName`, `CoverPhoto`, `AddToBag`, `ShopProductView` — do the
store's arithmetic, so a design never re-implements a price or a bag.

```osy title="the listing, the grid and a piece's page" test app=ui-building-a-look
namespace MyStore.Looks.Linen;

using Osysharp.Ui;
using Osysharp.Storage;
using Osysharp.Shop;
using Osysharp.Shop.Looks;

/// A listing: its name, a line about it, its sub-shelves as links, and the grid.
component LinenListing(string title, string lead, List<Product> products, List<Category> shelves, string current) : ListingPage {
  render {
    LinenSection {
      Stack(gap: 4, py: 10) {
        Text(title, fontFamily: Font.Display, fontSize: FontSize.Title);
        if (lead != "") { Text(lead, color: Colors.TextSecondary); }
        Row(gap: 4, wrap: Wrapping.Wrap) {
          foreach (var s in shelves) {
            Link(href: "/shop/" + s.Slug, label: s.Name) { Text(s.Name, color: s.Slug == current ? Colors.OnBg : Colors.TextMuted); }
          }
        }
        if (products.Count == 0) { Slot("empty"); } else { LinenGrid(products); }
      }
    }
  }
}

/// Pieces in a grid, each with its photograph, name and price.
[AllowAnonymous]
component LinenGrid(List<Product> products) : ProductGrid {
  live var shop = ShopSettings.FirstOrDefault();
  render {
    Box(display: Display.Grid, cols: "repeat(auto-fill, minmax(220px, 1fr))", gap: 5) {
      foreach (var p in products) {
        Link(href: "/p/" + p.Slug, label: p.Name) {
          Stack(gap: 2) {
            Image(src: File.Url(CoverPhoto(p.Images)), alt: p.Name, w: "100%", aspectRatio: "4 / 5", objectFit: ObjectFit.Cover);
            Text(p.Name);
            Text(LookPrice(p.Price, shop), color: Colors.TextMuted);
          }
        }
      }
    }
  }
}

/// One piece's page: its photograph, name and price, and the way into the bag.
[AllowAnonymous]
component LinenProductPage(Product? product, string note = "") : ProductPage {
  live var shop = ShopSettings.FirstOrDefault();
  live var first = product == null ? null : product.Variants.OrderBy(v => v.Rank).FirstOrDefault();
  string problem = "";
  action Add() { problem = first == null ? "This piece cannot be bought." : AddToBag(first, 1); }
  render {
    if (product == null) {
      LinenNotFound("That piece is not in the shop.", linkLabel: "Back to the shop", to: "/shop");
    } else {
      LinenSection {
        Stack(gap: 4, py: 10, maxW: "640px") {
          Image(src: File.Url(CoverPhoto(product.Images)), alt: product.Name, w: "100%", objectFit: ObjectFit.Cover);
          Text(product.Name, fontFamily: Font.Display, fontSize: FontSize.Title);
          Text(LookPrice(product.Price, shop));
          ShopProductView(product);
          Button("Add to bag", onPress: Add, tone: Tone.Primary);
          if (problem != "") { Text(problem, role: UiRole.Alert, color: Colors.Primary); }
          if (note != "") { Text(note, color: Colors.TextMuted); }
        }
      }
    }
  }
}
```

The shop's own looks (`kits/look-*`) go further on this page — sizes as chips, every photograph, the spec sheet, "notify me when
it is back" — and are the place to copy from.

### What happens to a type the look skips? {#skipped}
Draw only the block types you want in your own design. For the rest:

| the type | while a store wears your look |
|---|---|
| a block type with a plain design — in a shop, every type (the shop kit draws them) | drawn in that plain design, in YOUR theme's tokens |
| a block type no design draws | not offered to staff on the desk; one already placed waits in the arrangement and is listed before a switch |
| a shop PART (the frame, a page) | drawn by the store's default look's design of it, in YOUR tokens — so draw all seven ([#parts](#parts)) |

That is why Linen designs only the hero: the product row, the shelves, the quote and the footer's blocks come out of
the shop kit's plain designs dressed in Linen's palette, type and radii. Add a design of one later and every store that
installed the look draws the new one on its next compile — what staff arranged does not move.

### How does it ship its fonts? {#fonts}
A theme NAMES a family; the font file is what makes it render ([web fonts — shipping a typeface with your app](https://osysharp.com/reference/ui/web-fonts/)). Put the files in the kit's `fonts/`
and register each in the kit's own lock, from the kit's folder:

```console
$ cd looks/linen
$ osy font add fonts/Fraunces.ttf --family "Fraunces"
$ osy font add fonts/HankenGrotesk.ttf --family "Hanken Grotesk"
```

Keep each font's licence beside it (the looks in this repository ship `OFL-*.txt`). The look's fonts ship with every
compile of a store that installs it — pinned from a workspace or from the published package alike: publishing packs the
files under `fonts/` with the record of which family each one is, read from the kit's own lock. Two looks naming one
face upload it once.

### How do I see it in a store? {#see-it}
A look has no data of its own, so it is seen in a store. Install it in one — the Vall fashion store wears five looks,
the Second Round thrift store five more — and write a test that switches the version's look and photographs every page
a customer walks through, at a phone's width and a wide screen's. This is the walk `Apps/fashion-store/vall` runs for
each of its looks:

```osy title="switch the version to the look, then photograph the store" syntax
void WalkThrough(string look, string front) {
  runas (Staff) { Assert.Equal("", SetLook(MainVersion().Id, look)); }
  Visit(look, front, 390, 844, "phone");
  Visit(look, front, 1440, 900, "wide");
}

void Visit(string look, string front, int width, int height, string called) {
  Ui.Viewport(width, height);
  Ui.Visit("/");
  Assert.Visible(front);
  Ui.Shot($"{look} — the front, {called}");
  Ui.Visit("/p/cotton-trench-coat");
  Ui.Click("M");
  Ui.Click("Add to bag");
  Ui.Shot($"{look} — a piece, {called}");
}

[Test(Window)]
void the_store_in_linen() { WalkThrough("linen", "Woven slowly."); }
```

```console
$ osy test --pixels --filter the_store_in_linen
```

`--pixels` runs the walk in a real browser and writes each `Ui.Shot` to `osy-shots/`. Open them and look: a page that
passes every assertion can still put a button off the card, and only a picture shows it. `SetLook` answers why not, or
`""`; `LookLeavesBehind(version, look)` lists what your look has no design or region for ([[ui-looks#switch]]).

### Where would a café's contract sit? {#category}
The shop's contract is ONE category's. Another category — a café, a salon, a restaurant — declares its own beside it,
on the same site contract: the block types only it has (a menu board, a table booking) and the parts of its site (its
frame, its front page). A look for cafés then draws `Osysharp.Site.Looks` and the café's contract, and a store or a café
installs the looks of its own category.

```osy title="a café's contract, and a look that draws it with the site contract" test app=ui-building-a-look-cafe
app Corner {
  use Osysharp.Ui;
  use Osysharp.Blocks;
  use Osysharp.Site.Looks@0;
  model "model/**/*.osy";
}

using Cafe.Looks;

app.Look = Morning;

[Route("/")]
[AllowAnonymous]
component Front() { render { CafeFrame { CafeFront(); } } }
```

```osy title="the café's contract: one block type of its own and two parts" test app=ui-building-a-look-cafe
namespace Cafe.Looks;

using Osysharp.Blocks;

/// Today's menu on the wall — a heading and the dishes, one per line.
[Block("Menu board")]
public abstract component MenuBoard(
    [Label("Heading"), MaxLength(60), Sample("Today")] string title,
    [Label("The dishes"), MaxLength(800), Hint("One dish a line, with its price.")] Markdown dishes = "");

/// The frame around every page of a café.
public abstract component CafeFrame();

/// A café's front page.
public abstract component CafeFront();
```

```osy title="Morning — a café look drawing both contracts" test app=ui-building-a-look-cafe
namespace Corner.Looks.Morning;

using Osysharp.Ui;
using Osysharp.Blocks;
using Osysharp.Site.Looks;
using Cafe.Looks;

/// Chalk on slate, and the smell of the first loaf.
look Morning { Label = "Morning"; Theme = MorningTheme; }

theme MorningTheme { Colors { Bg = "#22262A"; OnBg = "#F2EFE8"; Surface = "#2E3338"; OnSurface = "#F2EFE8"; } }

component MorningFrame() : CafeFrame {
  render {
    Stack(gap: 0, bg: Colors.Bg, color: Colors.OnBg, minH: "100vh") {
      Region("announcement", allow: [Announcement]) { Announcement(words: "Open from seven"); }
      Slot;
      Region("footer", allow: [FooterWords, OpeningHours, Address]) { OpeningHours(); }
    }
  }
}

component MorningFront() : CafeFront {
  render {
    Region("front", allow: [Hero, MenuBoard, Quote]) {
      Hero(title: "Bread from six, coffee from seven.");
      MenuBoard(title: "Today", dishes: "Cardamom bun · 35\nSoup of the day · 95");
    }
  }
}

component MorningHero(string title, Markdown words = "", Picture photo = "", Picture second = "", string button = "Shop now",
                      PageLink link = "/", Color ground = Colors.Surface) : Hero {
  render { Text(title, p: 10, fontSize: FontSize.Heading); }
}

component MorningMenuBoard(string title, Markdown dishes = "") : MenuBoard {
  render { Stack(gap: 2, p: 8, bg: Colors.Surface) { Text(title, fontSize: FontSize.Title); Markdown(dishes); } }
}
```

Here the contract sits in the app, to keep the example in one place. As a kit it ships the way the shop's does: inside
the café kit, in a namespace of its own, because its types name the café's data (a dish, a table) — and the café kit
carries the PLAIN design of each of its types, which is what a café without a look draws, and what a look that skips a
type falls back to. A design of the site contract's types (the strip, the footer's opening hours) belongs with it too:
in a café without the shop kit, nothing else draws them.

### What does a finished look have? {#checklist}
- a `package.osy` that uses `Osysharp.Site.Looks` and its category's contract, and a summary that names the category;
- one `look` in a namespace of its own, naming a theme that sets the tokens a store's pages read;
- a design of every PART of its category (for a shop: all seven);
- a design of each block type it wants in its own hand, and blocks of its own where it has an idea no contract holds;
- regions and a menu under the names every look uses, so arrangements carry across;
- its fonts in its own lock, with their licences;
- a README whose first line says what it covers — *"A look for shops: draws Osysharp.Site.Looks and
  Osysharp.Shop.Looks"* — and a picture of a store wearing it, at a phone's width and a wide screen's.

## See also   {#see-also}
- [Looks — a site's whole design, picked per version](https://osysharp.com/reference/ui/looks/) — looks, contracts and the switch: the reference this page is the walk-through for.
- [The PageBuilder kit — the desk staff arrange the site at](https://osysharp.com/reference/ui/pages-kit/) — the desk: versions, the look each wears, the preview.
- [The site's frame — a menu, a strip and a footer staff change](https://osysharp.com/reference/ui/site-frame/) — the menu, the strip and the footer a frame declares.
- [Blocks and regions — pages the staff arrange](https://osysharp.com/reference/ui/blocks/) — blocks, regions, and what a value may be.
- [theme tokens](https://osysharp.com/reference/ui/theming/) — tokens, modes and variants.
- [web fonts — shipping a typeface with your app](https://osysharp.com/reference/ui/web-fonts/) — shipping a typeface.
- [package.osy](https://osysharp.com/reference/project/package/) — a kit's manifest, `osy lock` and publishing.
