> ## Documentation Index
> Fetch the complete documentation index at: https://docs.graphy.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Mappings & aesthetics

A **mapping** binds your data columns to **aesthetics** — the visual properties a chart draws with, like horizontal position, vertical position, and color. It's the bridge between a row of data and what's drawn on screen.

```tsx theme={null}
createSpec({ x: 'month', y: 'revenue', color: 'product' });
```

This reads: put `month` on the x-axis, `revenue` on the y-axis, and give each `product` its own color. The strings are column [keys](/sdk-next/data-structure).

## Built-in aesthetics

These are the built-in aesthetics you can map:

| Aesthetic     | Encodes                                                       | Typical column                       |
| ------------- | ------------------------------------------------------------- | ------------------------------------ |
| `x`           | Horizontal position                                           | Category, date, or number            |
| `y`           | Vertical position                                             | Number                               |
| `color`       | Fill / stroke color                                           | Category (or number, for a gradient) |
| `size`        | Size of each observation                                      | Number                               |
| `alpha`       | Opacity (0–1)                                                 | Number                               |
| `strokeWidth` | Line/border thickness                                         | Number                               |
| `lineType`    | Dash pattern (solid, dashed, dotted)                          | Category                             |
| `group`       | Splits observations into groups **without** a visual encoding | Category                             |

Not every geom reads every aesthetic — a `size` mapping means something to `geom.point()` but nothing to `geom.line()`. Each [geom](/sdk-next/concepts/geoms) documents the aesthetics it uses.

### `group` vs `color`

Both `color` and `group` split data into series. The difference: `color` also assigns a visual (a hue per category, with a legend); `group` only separates the observations. Use `group` when you want, say, one line per category but a single color for all of them.

## Variables and constants

An aesthetic can be bound to a **variable** (a column, read per row) or a **constant** (one literal value applied to every observation):

```tsx theme={null}
createSpec({
  y: 'revenue', // variable — shorthand for { variable: 'revenue' }
  color: { value: 'red' }, // constant — every observation is red
});
```

* `'revenue'` — string shorthand for a variable mapping.
* `{ variable: 'revenue' }` — the explicit variable form.
* `{ value: 'red' }` — a constant. The value still flows through the aesthetic's [scale](/sdk-next/concepts/scales), so `{ value: 'Pro' }` on `color` resolves to whatever color the scale assigns the `Pro` category.

## Where a mapping lives

A mapping can sit at two levels:

**Spec-level** — shared by every layer. Set it with `createSpec({ ... })`:

```tsx theme={null}
pipe(
  createSpec({ x: 'month', y: 'revenue' }),
  geom.line(),
  geom.point(), // both layers inherit x and y
  scale.x(),
  scale.y()
);
```

**Layer-level** — merged over the spec-level mapping for one geom only, via a geom's `aes` option. This is how combos give each layer a different `y`:

```tsx theme={null}
pipe(
  createSpec({ x: 'month' }), // shared x
  geom.bar({ aes: { y: 'revenue' } }), // this layer's y
  geom.line({ aes: { y: 'profit' } }), // a different y
  scale.x(),
  scale.y(),
  scale.ySecondary()
);
```

Layer mappings win where they overlap; anything they don't set falls through to the spec-level mapping.

## Every mapped position needs a scale

Mapping a column to `x` or `y` is only half the story — you must also declare the [scale](/sdk-next/concepts/scales) that turns those values into positions. `scale.x()` and `scale.y()` are never created automatically.

<Warning>
  A mapped position aesthetic with no matching `scale.*()` produces `NaN`
  positions. If a chart renders blank, a missing scale is the first thing to
  check.
</Warning>

## Next

* [Scales](/sdk-next/concepts/scales) — turn mapped values into positions and colors
* [Geoms & layers](/sdk-next/concepts/geoms) — which aesthetics each geom reads
