# Axis

Import `Axis` from `@pibbl/core/viz`. Supply `scale: ScaleId` and `position`:
`"bottom"`, `"left"`, `"top"`, or `"right"`. Linear, UTC, and band scales are supported.
The bottom/right baseline uses the frame height/width; top/left uses zero.
Endpoints follow the scale range. Ticks extend outward by 6 pixels, with labels
another 4 pixels away. Reversing a scale changes mapping, not the frame edge.
Axis does not reserve layout space: allocate label gutters yourself.

## Tick selection

Supply either signal-capable `tickCount` or `tickValues`, never both.

`tickCount` defaults to 5 and must be an integer from 0 through 100. Zero emits
no ticks or labels; one selects the first domain endpoint/category. Linear
counts of two or more are evenly spaced and include endpoints. Band counts are
a maximum: empty domains emit none, singleton domains emit one for any positive
count, and larger selections use evenly rounded domain indices with both
endpoints included. Band ticks are centered in their bands. Selection preserves
domain order, including when the physical range is reversed.

`tickValues` is an explicit array of at most 100 numbers or strings. An empty
list emits no ticks or labels. Numeric scales require finite numbers within the
inclusive domain extent, including descending domains. Band values must be known
categories; numeric `1` and string `"1"` are distinct. Duplicates reject (`0` and
`-0` count as equal). Invalid values are never coerced, clamped or silently
removed. The complete list is validated before formatting or tick geometry
allocation. Caller order is preserved; ticks are not sorted or deduplicated.

`tickFormat(value: number | string, index: number)` returns a string and receives
the emitted value and its emitted-order index. Numeric default labels use six
significant digits with trailing zeros removed; strings are unchanged. Two
values may format to the same text. The baseline remains visible with no ticks.
UTC ticks use calendar-aware generation and default to full ISO text. For readable numeric intervals, pass `linearTicks(domain)` as `tickValues`.

```tsx
import { Axis } from "@pibbl/core/viz";

<>
  <Axis scale="categories" position="left" tickValues={["North", "South"]} />
  <Axis
    scale="values"
    position="top"
    tickCount={5}
    tickFormat={(value) => (typeof value === "number" ? `${value}%` : value)}
  />
</>;
```

## Style and cost

`AxisStyle` supports `stroke` and `fill` (both default `#64748b`), `strokeWidth`
(default 1), `font` (default `12px sans-serif`), and `opacity` (default 1).
Whole styles and direct fields accept core signals. Width must be finite and
nonnegative; opacity must be between 0 and 1. Styles remain local.

Axis output is decorative and uses `pointerEvents="none"`. Work is O(k) for
k emitted ticks, bounded by 100 labels and one combined path. Generated band
selection does not copy or scan the full domain; scale construction owns domain
validation. There is no text-fit loop, retained cache or additional scheduler.

## Label collisions

`labelOverlap?: SignalValue<"allow" | "skip">` defaults to `"allow"` for
compatibility. `"skip"` measures labels with the axis font and greedily keeps
non-overlapping labels in physical range order, prioritizing the two outermost
tick positions. If even those collide, the physically first one wins.
`labelGap?: SignalValue<number>` defaults to 8 logical pixels and must be finite
and nonnegative. Invalid policies throw `TypeError`; invalid gaps throw
`RangeError`. Both signals are read by the Axis component.

Skipping affects label paint only: tick positions, tick strokes, scale domain,
data marks, and inspection coordinates remain unchanged. Formatting receives
original tick indices, including hidden labels. Vertical axes measure text
height; horizontal axes measure width. This does not reserve gutters. Long-label policies are described below. Custom fonts must be ready before mounting, or the application must
invalidate after loading them. See [numeric helpers](/reference/components/numeric-ticks/).

## Long labels

All the following inputs accept plain values or signals read by Axis:

| Input             | Default                  | Contract                                                             |
| ----------------- | ------------------------ | -------------------------------------------------------------------- |
| `labelOverflow`   | `"allow"`                | `"allow"`, `"wrap"`, or `"truncate"`; presentation policy only       |
| `labelMaxWidth`   | omitted                  | Positive finite logical-pixel line width; required for wrap/truncate |
| `labelMaxLines`   | `2`                      | Integer 1–20; limits wrapped lines                                   |
| `labelLineHeight` | measured font height + 4 | Positive finite logical-pixel line advance                           |

Truncation fits a measured ellipsis. Wrapping breaks at whitespace, then at
Unicode grapheme boundaries for oversized words. Whitespace is normalized for
wrapping. Excess lines truncate with an ellipsis. If even an ellipsis cannot fit,
the line is empty. Full values remain in the scale/domain and formatter input;
provide a visible detail readout or application-owned semantic table where full
labels are essential. Axis itself is decorative and has no interaction target.

Top/bottom labels extend outward into the caller's gutter, with lines in reading
order. Left/right labels are vertically centered on their tick. Collision skipping
measures the final multiline block. A small explicit line height can intentionally
overlap lines; the default leaves spacing. Invalid policy throws `TypeError`;
invalid width, line count, or line height throws `RangeError`, including invalid
optional values supplied with `"allow"`.

```tsx
<Axis
  scale="categories"
  position="left"
  labelOverflow="wrap"
  labelMaxWidth={120}
  labelMaxLines={3}
  labelOverlap="skip"
/>
```

Use [the long-label example](/playground/#/examples/composition/viz-long-labels) to compare
wrapping and truncation in narrow panels and shallow embeds.

### Preserve identity in responsive charts

Collision skipping is an opt-in density tool, not a general responsive layout
strategy. Numeric axes can often omit intermediate labels while retaining
readable scale context. Categorical labels identify marks: removing names can
make an otherwise unclipped chart unreadable. Preserve those labels by allocating
more plot space, reducing decorative header space, wrapping, changing orientation,
or offering an explicit paged/detail presentation when the full dataset cannot
fit. Do not merely switch off collision handling while leaving overlapping text.
Test both label completeness and non-overlap at narrow and shallow allocations.

## API details from source

<span id="api-Axis"></span>

### Axis

Draws ticks and labels using a named scale and the configured orientation and style.

```ts
Axis: (props: AxisProps) => ((JSX.Element[] | null)[] | JSX.Element)[]
```

Related API: [Axis](/reference/components/axis/), [AxisProps](/reference/components/axis/), [JSX](/reference/overview/).

#### Parameters

- **`props`** — Scale, orientation, tick, label, and styling options. See [AxisProps](/reference/components/axis/).

#### Returns

Pibbl nodes drawing the axis.

#### See also

[AxisProps](/reference/components/axis/)

[View source — packages/core/src/features/viz/lib/axis.tsx:18](/source/packages/core/src/features/viz/lib/axis-tsx/#L18)

<span id="api-AxisStyle"></span>

### AxisStyle

Supported geometry and presentation properties for Axis.

```ts
interface AxisStyle
```

Related API: [AxisStyle](/reference/components/axis/).

#### See also

[StrokeStyle](/reference/types/styles/#strokestyle)

[FillStyle](/reference/types/styles/#fillstyle)

[View source — packages/core/src/features/viz/lib/types.ts:312](/source/packages/core/src/features/viz/lib/types-ts/#L312)

#### Properties and methods

<span id="api-AxisStyle-stroke"></span>
<details>
<summary>stroke (optional)</summary>


```ts
readonly stroke?: StrokeStyle | undefined
```

Related API: [StrokeStyle](/reference/types/styles/#strokestyle).

Paint used for the outline. See StrokeStyle.

[View source — packages/core/src/features/viz/lib/types.ts:314](/source/packages/core/src/features/viz/lib/types-ts/#L314)

</details>

<span id="api-AxisStyle-strokeWidth"></span>
<details>
<summary>strokeWidth (optional)</summary>


```ts
readonly strokeWidth?: number | undefined
```

Width of the painted outline. See AxisStyle.

[View source — packages/core/src/features/viz/lib/types.ts:316](/source/packages/core/src/features/viz/lib/types-ts/#L316)

</details>

<span id="api-AxisStyle-fill"></span>
<details>
<summary>fill (optional)</summary>


```ts
readonly fill?: FillStyle | undefined
```

Related API: [FillStyle](/reference/types/styles/#fillstyle).

Paint used for the interior. See FillStyle.

[View source — packages/core/src/features/viz/lib/types.ts:318](/source/packages/core/src/features/viz/lib/types-ts/#L318)

</details>

<span id="api-AxisStyle-font"></span>
<details>
<summary>font (optional)</summary>


```ts
readonly font?: string | undefined
```

Canvas font string used to draw or measure text. See AxisStyle.

[View source — packages/core/src/features/viz/lib/types.ts:320](/source/packages/core/src/features/viz/lib/types-ts/#L320)

</details>

<span id="api-AxisStyle-opacity"></span>
<details>
<summary>opacity (optional)</summary>


```ts
readonly opacity?: number | undefined
```

Related API: [opacity](/reference/types/filters/#opacity).

Opacity of the painted result. See AxisStyle.

[View source — packages/core/src/features/viz/lib/types.ts:322](/source/packages/core/src/features/viz/lib/types-ts/#L322)

</details>

<span id="api-AxisProps"></span>

### AxisProps

Authored inputs for Axis, including the declared data and presentation options.

<details>
<summary>Full type declaration</summary>

```ts
type AxisProps = AxisBaseProps &
  (
    | {
        /** Explicit values at which axis ticks and labels are drawn. See {@link SignalValue}. */
        readonly tickValues: SignalValue<readonly (number | string)[]>;
        /** Not accepted in this variant; use the alternative fields instead. See {@link AxisProps}. */
        readonly tickCount?: never;
      }
    | {
        /** Not accepted in this variant; use the alternative fields instead. See {@link AxisProps}. */
        readonly tickValues?: never;
        /** Requested number of generated axis ticks. See {@link SignalValue}. */
        readonly tickCount?: SignalValue<number>;
      }
  )
```

</details>

Related API: [AxisProps](/reference/components/axis/), [SignalValue](/reference/types/signal-inputs/#signalvalue).

#### See also

[SignalValue](/reference/types/signal-inputs/#signalvalue)

[Axis](/reference/components/axis/)

[View source — packages/core/src/features/viz/lib/types.ts:366](/source/packages/core/src/features/viz/lib/types-ts/#L366)

#### Properties and methods

<span id="api-AxisProps-scale"></span>
<details>
<summary>scale</summary>


```ts
readonly scale: ScaleId
```

Related API: [ScaleId](/reference/components/scale/).

Identifier of the scale whose domain and range define the axis. See ScaleId.

[View source — packages/core/src/features/viz/lib/types.ts:332](/source/packages/core/src/features/viz/lib/types-ts/#L332)

</details>

<span id="api-AxisProps-position"></span>
<details>
<summary>position</summary>


```ts
readonly position: "bottom" | "left" | "right" | "top"
```

Position of the geometry, source, or selected resize handle. See AxisBaseProps.

[View source — packages/core/src/features/viz/lib/types.ts:334](/source/packages/core/src/features/viz/lib/types-ts/#L334)

</details>

<span id="api-AxisProps-tickFormat"></span>
<details>
<summary>tickFormat (optional)</summary>


```ts
readonly tickFormat?: ((value: number | string, index: number) => string) | undefined
```

Formats a tick's domain value for display as a label. See AxisBaseProps.

##### Parameters

- **`value`** — Numeric domain value or band category for the tick.

- **`index`** — Zero-based tick index.

##### Returns

Text to draw for the tick label.

[View source — packages/core/src/features/viz/lib/types.ts:341](/source/packages/core/src/features/viz/lib/types-ts/#L341)

</details>

<span id="api-AxisProps-labelOverlap"></span>
<details>
<summary>labelOverlap (optional)</summary>


```ts
readonly labelOverlap?: SignalValue<"allow" | "skip"> | undefined
```

Related API: [SignalValue](/reference/types/signal-inputs/#signalvalue).

Label collision policy; allow preserves all labels (default), skip measures and omits overlaps without removing ticks.

[View source — packages/core/src/features/viz/lib/types.ts:343](/source/packages/core/src/features/viz/lib/types-ts/#L343)

</details>

<span id="api-AxisProps-labelGap"></span>
<details>
<summary>labelGap (optional)</summary>


```ts
readonly labelGap?: SignalValue<number> | undefined
```

Related API: [SignalValue](/reference/types/signal-inputs/#signalvalue).

Minimum distance between visible labels in logical pixels; defaults to 8.

[View source — packages/core/src/features/viz/lib/types.ts:345](/source/packages/core/src/features/viz/lib/types-ts/#L345)

</details>

<span id="api-AxisProps-labelOverflow"></span>
<details>
<summary>labelOverflow (optional)</summary>


```ts
readonly labelOverflow?: SignalValue<"allow" | "truncate" | "wrap"> | undefined
```

Related API: [SignalValue](/reference/types/signal-inputs/#signalvalue).

Long-label policy, default allow. Wrap/truncate require labelMaxWidth; coordinates remain unchanged.

[View source — packages/core/src/features/viz/lib/types.ts:347](/source/packages/core/src/features/viz/lib/types-ts/#L347)

</details>

<span id="api-AxisProps-labelMaxWidth"></span>
<details>
<summary>labelMaxWidth (optional)</summary>


```ts
readonly labelMaxWidth?: SignalValue<number> | undefined
```

Related API: [SignalValue](/reference/types/signal-inputs/#signalvalue).

Maximum measured line width in logical pixels, positive and finite. Required for wrap/truncate.

[View source — packages/core/src/features/viz/lib/types.ts:349](/source/packages/core/src/features/viz/lib/types-ts/#L349)

</details>

<span id="api-AxisProps-labelMaxLines"></span>
<details>
<summary>labelMaxLines (optional)</summary>


```ts
readonly labelMaxLines?: SignalValue<number> | undefined
```

Related API: [SignalValue](/reference/types/signal-inputs/#signalvalue).

Maximum wrapped lines, integer 1–20; default 2. Excess text receives an ellipsis.

[View source — packages/core/src/features/viz/lib/types.ts:351](/source/packages/core/src/features/viz/lib/types-ts/#L351)

</details>

<span id="api-AxisProps-labelLineHeight"></span>
<details>
<summary>labelLineHeight (optional)</summary>


```ts
readonly labelLineHeight?: SignalValue<number> | undefined
```

Related API: [SignalValue](/reference/types/signal-inputs/#signalvalue).

Positive line advance in logical pixels; default is measured font height plus 4.

[View source — packages/core/src/features/viz/lib/types.ts:353](/source/packages/core/src/features/viz/lib/types-ts/#L353)

</details>

<span id="api-AxisProps-style"></span>
<details>
<summary>style (optional)</summary>


```ts
readonly style?: SignalValue<Readonly<{ readonly stroke?: SignalValue<StrokeStyle | undefined>; readonly strokeWidth?: SignalValue<number | undefined>; readonly fill?: SignalValue<FillStyle | undefined>; readonly font?: SignalValue<string | undefined>; readonly opacity?: SignalValue<number | undefined>; }>> | undefined
```

Related API: [SignalValue](/reference/types/signal-inputs/#signalvalue), [StrokeStyle](/reference/types/styles/#strokestyle), [FillStyle](/reference/types/styles/#fillstyle), [opacity](/reference/types/filters/#opacity).

Declared presentation and layout properties. See SignalValue, SignalStyle,
AxisStyle.

[View source — packages/core/src/features/viz/lib/types.ts:358](/source/packages/core/src/features/viz/lib/types-ts/#L358)

</details>

<span id="api-AxisProps-tickValues"></span>
<details>
<summary>tickValues (optional)</summary>


```ts
readonly tickValues?: SignalValue<readonly (string | number)[]> | undefined
```

Related API: [SignalValue](/reference/types/signal-inputs/#signalvalue).

Explicit values at which axis ticks and labels are drawn. See SignalValue.
Not accepted in this variant; use the alternative fields instead. See AxisProps.

[View source — packages/core/src/features/viz/lib/types.ts:370](/source/packages/core/src/features/viz/lib/types-ts/#L370)

</details>

<span id="api-AxisProps-tickCount"></span>
<details>
<summary>tickCount (optional)</summary>


```ts
readonly tickCount?: SignalValue<number> | undefined
```

Related API: [SignalValue](/reference/types/signal-inputs/#signalvalue).

Not accepted in this variant; use the alternative fields instead. See AxisProps.
Requested number of generated axis ticks. See SignalValue.

[View source — packages/core/src/features/viz/lib/types.ts:372](/source/packages/core/src/features/viz/lib/types-ts/#L372)

</details>

## Implementation guidance for agents

Read the [Data visualization companion](/agents/topics/visualization/) for ownership, adaptation, failure modes, and verification. [Agent start](/agents/) provides the version-selection workflow.

## Complete minimal examples

- [Scales and axes](/minimal-examples/viz/scales/): Create band and linear scales with a typed axis and inspect resolved mappings. [Plain source](/minimal/viz/scales.tsx)
## Documentation version

Documentation built with @pibbl/core 0.0.2, revision 272a94a. ALPHA — NOT FOR PRODUCTION USE.
