Componentsv2.189.0
Iconsv1.19.0
MCPv0.8.61
Tokensv0.34.3

Contributing

Learn how to contribute, including general guidelines, running the project locally, publishing, and creating new components.


We’re excited that you’d like to contribute to Pluma. We are currently only accepting contributions from Customer.io employees.

Running locally

Pluma uses pnpm workspaces. To use the expected tool versions, run:

mise trust
mise install --yes

Install dependencies and build the workspace from the repo root:

pnpm install
pnpm run build:all

For day-to-day development, start the full local environment:

pnpm run dev

For docs-only work, run:

pnpm run dev:docs

Publishing

Most changes that affect published packages need a changeset. Create one from the repo root:

pnpm exec changeset

Select the changed package, choose the version bump, and commit the generated changeset file with your PR. Changesets will use that file later to update package versions, changelogs, and published packages.

If you need to test a package in another app before release, create a snapshot build after adding a changeset:

pnpm run build:all
pnpm exec changeset version --snapshot my-feature
pnpm exec changeset publish --no-git-tag --tag my-feature

Snapshot version changes are local publishing artifacts and should not be committed.

Creating new components

Use the component generator from the repo root:

pnpm exec hygen component new component-name

The generator creates the React and Ember component files, shared styles, docs, stories, and tests, then updates the relevant entrypoints. For a subcomponent, run:

pnpm exec hygen subcomponent new component-name --parent=parent-component-name

Component docs should live next to the component as {component-name}.docs.mdx. In docs examples, import Pluma modules through @customerio/pluma-components instead of relative source paths so examples match how consumers use the package.

Component API

We have some rules around how to design a good API for a component.

Property names

When adding a new property or changing an existing one, you should follow these rules:

  1. use the prefix is or has when affecting state - e.g. isOpen, isHovered, hasError
  2. use the prefix on for events or callbacks - e.g. onOpen, onClose, onHover
  3. Use the prefix should when affecting behavior - e.g. shouldCloseOnOverlayClick, shouldCloseOnEscape
  4. Use the prefix aria when affecting accessibility - e.g. ariaLabel, ariaDescribedBy
  5. Use the prefix with when conditionally rendering something - e.g. withIcon, withArrow, withOverlay

Specific names:

  1. Use isError when affecting the error state
  2. Use isDanger when the style indicates a destructive action
  3. Use isDisabled when the component is disabled

Color naming

To ensure consistency, use semantic names like critical, success, caution, and information instead of raw color names like red, yellow, or blue whenever possible. Colors that don't have a semantic name should use their presentational color name (i.e. plum).

Accessibility

Components should make it easy to get accessibility right. This means:

  1. Use the aria-* attributes when necessary
  2. Use the correct role attribute
  3. Use the tabIndex attribute

Follow the ARIA Authoring Practices Guide (APG) when creating a new component.

Size property

When designing a component with multiple sizes you should probably use a size property. It should take shorthand values. For example, size="sm" or size="lg". Long hand values (i.e. size="small") are not extensible and should be avoided.

The one exception to this is font weights where we follow browser standards and use size="normal", size="bold", etc.

Variant property

When designing a component with multiple variants you should probably use a variant property. It should almost always just take the following:

  • primary
  • secondary
  • tertiary
  • subtle

Danger variants should be done through a isDanger property, so it can be combined with other variants.

By default, variant should be secondary.

Composite components

Whenever you are creating a component that can contain multiple parts (i.e. a tooltip has a trigger and content), you should use multiple components that compose together. This allows us to have the same API between React and Ember. If most of the use-cases are strings for one of the parts, add a property that takes a string and does the composition to make it easier for the developer to get right.