Skip to content

[Feature] Readable outside labels for crowded pie and donut charts #227

Open

Description

@hshoja

Problem

When a pie has many small, adjacent slices, outside labels made with
radialRule + radialText are placed on their slice angle and overlap.
There is no built-in way to spread or hide them, so apps must write their own
pixel layout.

const browsers = [
  { name: 'Chrome', share: 64.2 }, { name: 'Safari', share: 18.9 },
  { name: 'Edge', share: 5.4 }, { name: 'Firefox', share: 3.1 },
  { name: 'Samsung', share: 2.4 }, { name: 'Opera', share: 2.0 },
  { name: 'UC', share: 1.2 }, { name: 'Yandex', share: 0.9 },
  { name: 'Vivaldi', share: 0.7 }, { name: 'Brave', share: 0.6 },
  { name: 'Other', share: 0.6 },
]
Image

Proposal

A radialLabels polar mark that draws outside labels with elbow connectors:

  • labels stack on each side of the pie, in slice order;
  • overlapping labels move apart as little as possible, inside the chart bounds;
  • when a side is full, the lowest-priority labels hide first (default: value, so the smallest slices);
  • optional maxShift hides labels that would move too far from their slice.
radialLabels(pie(browsers, { value: 'share' }), {
  text: (row) => `${row.name} ${row.share}%`,
  key: 'name',
  color: 'name',
  maxShift: 24,
})
Image Image

I have a working implementation with tests and docs, and can open a PR if this direction fits.
Happy to change the API shape (for example, an option on radialText instead of a new mark).

Activity

  1. tannerlinsley commented on Oct 14, 2026

    @tannerlinsley
    Member

    Thanks @hshoja, please open a draft PR with the implementation so we can review it before choosing the API shape.

    Please include narrow-chart and resize cases, equal-priority ties, long labels, and what happens when maxShift hides a label. Stable keys and connector motion matter here too. The layout should work across renderers, and charts that do not use it should not pick up its bundle cost.

    I haven't settled on a separate mark versus a radialText option yet, so let's compare those with the code in front of us.

  2. hshoja commented on Oct 14, 2026

    @hshoja
    Author

    Thanks @tannerlinsley. Draft PR with the implementation and the requested cases: #231. The PR description lists each case, its behavior and test coverage, the bundle numbers, and a short comparison of a separate mark vs a radialText option.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions