> ## 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.

# Mekko

export const GraphEmbed = ({src, story, aspectRatio, borderRadius = '14px', args}) => {
  const STORYBOOK_BASE_URL = typeof window !== 'undefined' && window.location.hostname === 'localhost' ? 'http://localhost:6006' : 'https://storybook-sdk.vercel.app';
  function buildArgsString(args) {
    if (!args) return '';
    const encodeValue = value => {
      if (value === null) return '!null';
      if (value === undefined) return '!undefined';
      if (typeof value === 'boolean') return `!${value}`;
      if (typeof value === 'number') return String(value);
      return encodeURIComponent(String(value));
    };
    const properties = Object.entries(args).map(([key, value]) => `${key}:${encodeValue(value)}`).join(';');
    return `&args=${properties}`;
  }
  const resolvedSrc = story ? `${STORYBOOK_BASE_URL}/iframe.html?id=${story}&viewMode=story&embed=1&globals=mode:readonly${buildArgsString(args)}` : src;
  const resolvedAspectRatio = aspectRatio ?? (story ? '16 / 9' : '10 / 6');
  return <iframe src={resolvedSrc} loading="lazy" allowfullscreen="true" style={{
    width: '100%',
    aspectRatio: resolvedAspectRatio,
    border: 'none',
    borderRadius,
    colorScheme: 'light'
  }} />;
};

A mekko, or Marimekko chart, is a 100% stacked column chart whose columns are as wide as their totals. Each cell's area is then its share of the whole: how big each market is reads across, and each company's share of it reads up.

<Warning>
  The mekko ships as its own package, `@graphysdk/geom-mekko`, on a `0.x`
  version line. While it is on `0.x`, a minor release may change its aesthetics,
  style or look; pin a caret range (`^0.1.0`) to take only patches. Tell us what
  works and what doesn't on [Discord](https://discord.gg/yGmYCjkSr).
</Warning>

## Install

```bash theme={null}
npm install @graphysdk/geom-mekko
```

It peer-depends on `@graphysdk/react` and nothing else. Register the plugin once with `createGraphyKit`, which adds `kit.geom.mekko()` and binds `kit.GraphProvider` to the same plugin:

```tsx theme={null}
import {
  createGraphyKit,
  GraphRenderer,
  highlight,
  styles,
} from '@graphysdk/react';
import { mekko } from '@graphysdk/geom-mekko';

const kit = createGraphyKit({ plugins: [mekko] });
```

## A mekko chart

Pass one row per cell. Map the column to `x`, the cell's amount to `y` and its group to `color`:

<GraphEmbed story="chart-types-mekko--markets" args={{ orientation: 'vertical' }} />

```tsx theme={null}
const data = {
  columns: [
    { key: 'market', label: 'Market' },
    { key: 'company', label: 'Company' },
    {
      key: 'revenue',
      label: 'Revenue',
      valueFormat: { type: 'currency', iso: 'usd' },
    },
  ],
  rows: [
    { market: 'Retail', company: 'Acme', revenue: 420 },
    { market: 'Retail', company: 'Globex', revenue: 260 },
    { market: 'Energy', company: 'Acme', revenue: 90 },
    // …
  ],
};

const spec = kit.pipe(
  kit.createSpec({ x: 'market', y: 'revenue', color: 'company' }),
  kit.geom.mekko(),
  kit.scale.x.continuous(),
  kit.scale.y.continuous()
);

export function Markets() {
  return (
    <kit.GraphProvider spec={spec} data={data}>
      <GraphRenderer />
    </kit.GraphProvider>
  );
}
```

Each column is as wide as its total's share of the grand total, and the columns run in the order their first row comes. Each cell is as tall as its amount's share of its column, and the cells stack in the order their group first appears in the data, so a group holds the same place in every column. Rows that repeat a column and group are summed into one cell. A missing, zero, negative or non-finite amount draws no cell and adds nothing to its column.

## Column order

Pass `sort` to run the columns in another order: `'descending'` runs them largest first, `'ascending'` smallest first, and a list names them in order, any column it leaves out following in data order. An unknown `sort` is reported as a warning, and the columns keep data order.

```tsx theme={null}
kit.geom.mekko({ params: { sort: 'descending' } });
kit.geom.mekko({ params: { sort: ['Energy', 'Retail'] } });
```

## The share axis

Both axes run from 0 to 100%. The axis along the columns is continuous, so declare `scale.x.continuous()`, not `scale.x.discrete()`: a discrete axis gives every category the same width. It is ticked with each column's name at the column's center, and draws no grid line through the columns.

The other axis reads each cell's share of its column, in percentages. It has no title by default, since the share is a variable the geom writes; give it one in the config:

```tsx theme={null}
config({ axes: { y: { label: 'Share of market' } } });
```

## Horizontal

Add `kit.coord.flip()`. The columns run down the chart as rows, the first at the top, and the shares run across.

<GraphEmbed story="chart-types-mekko--markets" args={{ orientation: 'horizontal' }} />

## Styling

Every cell is one observation, styled with `kit.style.geom.mekko()`:

| Properties | Default |
| - | - |
| `fill`, `fillAlpha`, `alpha`, `stroke`, `strokeWidth`, and the rest of the shared geom vocabulary | The `color` scale's colour, a 1px border in the background's colour |

The cells abut, so the border is what parts them. A chart-wide `style.geom()` reaches the cells as it reaches a bar.

A cell's `fill` takes a pattern (`diagonal`, `crosshatch` or `dots`) or a gradient as well as a colour. A pattern replaces the colour the scale gives, so set it as an override, and pick a group out with `where`. Hatching every group but one lets the groups read apart without colour alone:

<GraphEmbed story="chart-types-mekko--styled" args={{ orientation: 'vertical', gutter: 4 }} />

```tsx theme={null}
const spec = kit.pipe(
  // …
  kit.scale.color.discrete({
    domain: ['Acme', 'Globex', 'Initech', 'Umbrella'],
    range: ['#0b2545', '#1d4e89', '#4f86c6', '#7fa7d9'],
  }),
  styles({
    defaults: [kit.style.geom.mekko({ strokeWidth: 4 })],
    overrides: [
      kit.style.geom.mekko(
        {
          fill: {
            pattern: 'diagonal',
            color: '#1d4e89',
            background: '#eef3fa',
          },
        },
        { where: { variable: 'company', eq: 'Globex' } }
      ),
      // … a `crosshatch` for Initech, `dots` for Umbrella
    ],
  })
);
```

The legend's swatches keep the scale's colours; they do not take a pattern set by an override.

## The share variables

The geom writes two shares as variables you can name in a `label`, a `highlight` or an entry's `where`. Both are fractions, in the percentage format:

| Variable | Value |
| - | - |
| `mekkoShareOfColumn` | The cell's share of its column: its height |
| `mekkoColumnShare` | The column's share of the total: its width |

## Hover and highlight

Hovering a cell emphasises it. The tooltip is headed by its column, and lists each group's share of that column, with the hovered one marked:

```
Retail
■ Acme       42%
■ Globex     26%
■ Initech    18%
■ Umbrella   14%
```

A highlight raises each matched cell, whether it matches a column of the data or a share the geom wrote:

<GraphEmbed story="chart-types-mekko--highlight" args={{ orientation: 'vertical', target: 'over a third of its market' }} />

```tsx theme={null}
const spec = kit.pipe(
  // …
  highlight({ variable: 'mekkoShareOfColumn', gt: 1 / 3 })
);
```

## Not yet supported

* **The editor's chart-type picker and the chart agent.** In editable mode the mekko renders, hovers, selects and takes annotations on a cell's center, but it is authored in code.
* **Amounts in the tooltip.** It gives each group's share of the column, not its amount or the column's share of the total.
* **Column labels that would overlap.** The share axis prints every column's name, however narrow its column; a very narrow column's name can run into its neighbour's.
* **Data labels.** `dataLabels` prints nothing on a cell yet.
* **Warnings for dropped amounts.** A zero, negative or non-finite amount is left out without a warning.
* **A discrete share axis.** Declaring `scale.x.discrete()` draws the cells in the wrong places, with no warning.
* **Polar coordinates.** A mekko draws under `coord.cartesian()` and `coord.flip()` only.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.