# color palettes

> `Palette.From("#seed")` turns one brand color into a full ramp of shades. A bare reference (`Primary`) is the seed itself; `Primary.Hover` and `Primary[600]` reach the named and numbered steps of its ramp.

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

## Summary        {#summary}
A **palette** turns a single brand color into a full **ramp** — a scale of shades from very light to very dark, plus
named steps for common jobs like a hover state. You give one seed color; the palette generates the rest, evenly, so a
button has a darker shade to hover to and a light tint to sit on without you hand-picking each one.

```osy title="one seed, a whole ramp" test app=ui-palette
theme Default {
  Colors {
    Primary = Palette.From("#0077B6");   // one seed → a whole ramp
  }
}
```

## Signature      {#signature}
```osy syntax
Primary = Palette.From("#0077B6");   // declare a palette from a seed color
Bg = Primary.Hover;                  // a named step
Bg = Primary[600];                   // a numbered step (50…950)
```

## Description    {#description}

### One seed, a full ramp   {#from}
`Palette.From("#seed")` declares a **palette token**. From the one seed color it generates an even ramp of shades —
computed in a perceptual color space, so the steps read as evenly spaced and the hue stays true from the lightest tint
to the darkest shade (a plain "lighten/darken" drifts and muddies; this does not).

```osy title="two ramps" test app=ui-palette-two
theme Brand {
  Colors {
    Primary = Palette.From("#0077B6");
    Accent  = Palette.From("#C0392B");
  }
}
```

The seed must be a hex color literal (`"#0077B6"` or the short `"#07B"`). Anything else — `Palette.From(Primary)`,
`Palette.From()` — is a compile error, so a mistyped palette can't slip through as an empty one.

### Getting the exact seed color back — the bare name   {#base}
Referencing the palette by its **bare name** gives you the **seed exactly as you typed it** — your brand color, not a
generated approximation of it:

```osy title="the ramp's base step" test app=ui-palette
[Composable] component Fill() {
  variants { base { Bg = Colors.Primary; } }   // Bg is exactly #0077B6
  render { Box(); }
}
```

Like any [theme tokens](https://osysharp.com/reference/ui/theming/) token reference, a bare palette name is a **living reference**, so restyling the seed carries
through everywhere it's used with no rebuild.

### Named steps   {#semantic-steps}
A palette exposes a small set of **named steps** for the jobs a color actually does in an interface. Reach them with a
member access:

```osy title="named steps" test app=ui-palette
[Composable] component Swatch() {
  variants {
    base {
      Bg = Colors.Primary; Color = Primary.OnColor;   // fill + a legible foreground on it
      Hover { Bg = Primary.Hover; }            // a step darker on hover
    }
  }
  render { Box(); }
}
```

| Step | What it's for |
|---|---|
| `Subtle` | a faint tint — a hover background, a selected row |
| `Muted` | a soft fill — a chip, a well |
| `Default` | the palette's mid shade |
| `Hover` | one step stronger than the base — a hover/emphasis fill |
| `Active` | stronger still — a pressed state |
| `Strong` | the darkest useful shade — high-emphasis text or borders |
| `OnColor` | the **foreground** color to place *on* the palette — chosen automatically (black or white) for legible contrast against the base |

`OnColor` is the one that isn't a shade of the hue: it's whichever of black or white reads clearly on your brand
color, worked out for you — so `Color = Primary.OnColor` is legible whether your brand is a deep navy or a pale amber.
Naming a step that doesn't exist (`Primary.Hund`) is a compile error listing the ones that do.

### Numbered steps   {#numeric-steps}
Under the named steps is a **numeric ramp** — `50` (lightest) through `950` (darkest), in the familiar `50, 100, 200 …
900, 950` scale. Use it when you want an exact step the names don't single out:

```osy title="numbered steps" test app=ui-palette-steps
theme Brand {
  Colors {
    Primary = Palette.From("#0077B6");
    Line    = Primary[200];    // a light hairline from the same ramp
    Ink     = Primary[900];    // near-black, still on-brand
  }
}
```

The named steps are aliases over this ramp (`Hover` is the `700` step, `Default` the `500`, and so on), so
`Primary.Hover` and `Primary[700]` are the same color — pick whichever reads better where you use it. A number outside
the `50…950` scale (`Primary[550]`) is a compile error listing the valid steps.

### Where palettes work   {#where}
A palette step is an ordinary style value, so it works anywhere a color does — a **theme token**:

```osy title="one token derived from another's step" test app=ui-palette-derive
theme Brand {
  Colors {
    Primary = Palette.From("#0077B6");
    Focus   = Primary.Active;    // derive one token from another palette's step
  }
}
```

…and a **`variants` style-prop** (as in the button above). In both, a palette step stays a **living reference** into the
ramp — change the seed and every shade derived from it moves with it, with no rebuild.

## See also   {#see-also}
- [theme tokens](https://osysharp.com/reference/ui/theming/) — the `theme` block, token groups, references, and dark mode.
- [component](https://osysharp.com/reference/ui/component/) — declaring a component and styling it with `variants`.
