Skip to content

Contributing

This is the path between I need something the system does not have and here is the answer. It exists so that someone other than the design lead can request a token or component and get an answer through a defined path — a system without one decays toward whoever shouts loudest. It is written for three readers: the person who needs something, the person who has a change in mind, and the person answering. Who decides is GOVERNANCE.md, and its one-sentence answer is: Two approvers — the design lead and the tech lead — with a monthly release note.

Nothing here asks you to know what a token is. Describe the work, not the system.

1. Asking — you need something the system does not have

Section titled “1. Asking — you need something the system does not have”

Who may ask. Anyone at Mobupps, for anything the system is supposed to hold: a colour, a type size, a component or an option on one, a pattern, a template, a rule — or a fix to something that looks wrong. A coding agent asks the same way, through the person running it: if the system has no token for what you need, stop and ask — do not invent a hue, a step or a size.

Where. The form Ask the design system for something. It opens for everyone signed in to the Mobupps Asana, and for nobody outside it. Each request becomes a task in the Asana board Pepper UI, section Intake · requests from anyone, and whoever sent it is added to that task as a collaborator — so the answer comes back to you there, although the board itself stays private.

If you do not use Asana. Tell the design lead, in the channel you already use, and they send the form for you while you watch, the same day: accept it warmly, write it in the intake section of the Asana board while they watch, and do not agree to a date.

What to say. The form asks, in this order — your name and email come with it by themselves:

  1. What you were trying to make.
  2. What you used instead, because the thing you needed didn’t exist.
  3. Where it shows up — a file, a URL, a slide — with a screenshot if you have one.

The second one is the request. The system is built from real product work, not for hypothetical work, and what you reached for when the right thing was missing is the most exact description of the gap there is. The last question, by when it matters, is optional — it is read, and it is not a date anyone agrees to (§5).

What is not a request. An override. A hex typed onto one slide, a font chosen from a reference, a fill painted on an instance to hide a scaling mistake: when an instance looks wrong, the fix is the component, never an override on the instance. The right move is the request; the override is what the request replaces, and a reviewer who finds one will answer with the token or component that should have been there.

What you get back. An answer, in one of four words (§3), within the promise in §5 — and the reason with it. Every answer stays on the card, so the board is also the record of what was asked and why it was or was not built: an intake that only accepts requests it intends to fulfil stops receiving requests.

2. Proposing — you have the answer in mind

Section titled “2. Proposing — you have the answer in mind”

A proposal is a request that arrives with its answer drafted. Three shapes, and what each must carry beyond §1’s three things:

You propose… Carry
a value — a new colour, size, spacing step, or a change to one the value and the role it plays (text/…, surface/…, accent/… — never a nicer blue); where it is used today; if it is a text or a surface, what it sits against, because every text/surface pair has a measured contrast floor
a component — new, or an option on an existing one the nearest existing component and why it does not fit — its contract says Use when · Do not use when · Relates to · Anti-patterns, so the answer is often already on the component’s page; a source master if one exists in Mobupps’ own work (the decks, the site, the products); the states it needs; one name, because Figma name = code name = doc name and a script checks it
a rule — a spacing, a pairing, a never the corpus it comes from (which slides, which screens), and what it forbids: a principle that forbids nothing is decoration. Locked rules become tests, not prose, so say what the test would measure

Where a proposal lives. The same Intake card, its title starting Proposal:. A design proposal points at a frame in your own file — never at an edit inside the library file Pepper UI, which only an approver writes to (§6). A code proposal may come as a branch on the repository (§6), but the card is what gets the answer, so open it first.

What a proposal cannot be. A change to a consumer. The deck tool, the site, MAFO and the Figma product files are read-only from here: Nothing is connected to a live consumer. Every proof is built on a copy, and a change for one of them is written up as a change request in artifacts/change-requests/ with its evidence and its verification steps (CR-001 is the model) and handed to its owner.

An answer is a comment on the card from an approver, and its first word is one of these four — so the roadmap page can count outcomes without a form field:

Answer It means The card
Exists the system already holds it, and the answer says where — a component page, a foundation page, a token by name. The commonest answer, and it is a good one: the request found a hole in the documentation, and that hole is fixed too closed
Queued it is a row in the build queue, marked NEXT and worked in the queue’s order. It is built to the same nine-point bar as everything else open until built, then closed with the page it became
Declined it contradicts a principle or a decision, and the answer names which — or it is an override dressed as a request (§1) and the answer names the token or component to use instead closed, the reason on it
Question the approver cannot decide yet and asks one thing back. It counts as the first answer (§5) and not as a decision; the decision follows the reply open

4. Reviewing — what an answer checks, in order

Section titled “4. Reviewing — what an answer checks, in order”

For the approver. The order matters because most requests end at the first step and none should reach the fourth without passing the second.

  1. Does it exist? The component board and the foundation pages on the documentation site; DESIGN.md for a value. If yes: Exists, with the link — and if the requester could not find it, the page or the contract is the thing to fix.
  2. Is it a request, or an override? A value the system has no role for is a request. A value that exists under another name, or a component detached to look different on one screen, is an override — Declined, naming what should carry it.
  3. Is it already a row? docs/10_COMPONENTS.md holds every component the system intends to have — Mobupps’ own and the 64 of the shadcn registry. If the row exists: Queued, with its position. If not, and the request is real: add the row, mark it NEXT, cite the request as its evidence — Queued.
  4. Does it contradict something decided? The twelve principles and the decisions log are what a request is held to; a request that would reopen one is Declined with the number, and the requester is told how a decision is reopened.
  5. What breaks? For a change to something that exists: which consumers read it — the changelog page lists every package’s surface and what each commit did to it — and which measured pairs move (contrast, focus ring, the Figma library’s bound fills). A rename is a breaking change, and the architecture’s rule is migration notes on every breaking rename.

Signatures. Two approvers, and the decision reads: The tech lead can be the second signature on a new token or component without writing any of it … a structural change gets a second opinion. So a new token, a new component, a rename or a removal takes both signatures; a fix within an existing component’s own values, or a documentation gap, takes one. GOVERNANCE.md §2 makes that final, and says who decides everything else. A direction — a new surface or product joining the system, a deliverable struck or added, a change that alters what managers make with it — is put to the stakeholders it reaches before the two signatures.

What an accepted change becomes. A value: a token in tokens/*.json with a $description naming the file it came from — a value with no $description naming the file it came from is not finished — then npm run build. A component: a queue row, then a Figma set and a stylesheet and a registry item built to the nine points of docs/10_COMPONENTS.md §The bar, carrying its contract. A rule: a test. Anything that decided something: a line in docs/03_DECISIONS.md with its date and its why. A Figma write is followed by the audit run, so the library’s score on the roadmap page reflects it.

5. How long — the promise, and the clock

Section titled “5. How long — the promise, and the clock”

The clock. A request is open from the moment the form creates its task in Intake. It is answered by the first comment an approver leaves on it — whichever of the four words it is — and decided when that word is Exists, Queued or Declined. Asana records who wrote a comment and when, and when a task was created and completed, so the two numbers the roadmap page has promised since the architecture was written — open requests, time-to-answer — are:

  • open requests — requests the form created that are not completed;
  • time to answer — from a task’s creation to its first approver comment, reported as the median and the longest.

No form field, no label, no custom status: the definition is the events Asana already keeps, so the form stays simple and the count cannot depend on anyone remembering to set a field. A request is told from any other card in Intake by the line Asana writes at the foot of every task the form creates — This task was submitted through Ask the design system for something — so the Read me first card and anything written there by hand are not counted. Time is counted in working days, the unit of the promise below. The roadmap page shows both numbers as read from the board when the site was generated, and says on which day; if an asker is ever missing from their own request’s task, the count stops rather than print a number for an answer that could not have reached them.

The promise. A first answer within ten working days — the two-week rhythm the roadmap already runs on: every two weeks: sprint review note (what shipped, what moved, what is blocked) posted to the Asana project. A decision by the next monthly release note. A build has no date: the queue is worked in order and slippage is logged rather than negotiated, and the roadmap page shows where every row stands. This is a promise on one part-time person’s time; it is stated so it can be measured, and the roadmap page says how often it was kept. Both numbers are the design lead’s to change.

6. Contributing to the source — code and Figma

Section titled “6. Contributing to the source — code and Figma”

For the day there is a second contributor. Today nobody but the design lead builds the system, and every rule below is already enforced by the build rather than by a reviewer’s memory.

  • The repository is bitbucket.org/rashidmobupps/pepper-ui, private, default branch main; access is by invitation, two people today. A change is a branch and a pull request; npm test and the pipeline must be green, and every generated output — packages/*/dist, DESIGN.md, the registry, the workshop, the documentation site, the changelog record — is held to its source by that test, so a source edited without its build re-run fails.
  • Edit the source, run the build. tokens/*.json → npm run build; a component’s stylesheet under packages/css/src/components/ opens with its Figma set and the markup to copy; the registry item carries the same contract as the Figma description; npm run registry, npm run docs:gen. A change under packages/tokens, packages/css or packages/charts re-runs python3 scripts/changelog.py before npm test, or the test fails naming it.
  • Never type a colour. A literal hex, a named colour or an rgb() in a package fails its own build. Every number cites its source.
  • Figma. The library file Pepper UI is written only by an approver, with Figma desktop open (a write into a file that is not open fails or half-applies), through the discipline in CLAUDE.md §Figma work; every session that touches it ends with the audit ritual and the snapshot, which the parity script reads.
  • Never a consumer. Pilots live in artifacts/pilots/; changes for a consumer are change requests (§2).
  • Agents. AGENTS.md is the note for any coding agent and CLAUDE.md the full brief; a decision made on the way goes to docs/03_DECISIONS.md with its why.

7. What this file does not decide, and what it waits on

Section titled “7. What this file does not decide, and what it waits on”
  • Who decides — GOVERNANCE.md.
  • The release cadence — semver, the monthly minor, migration notes on every rename — RELEASING.md.
  • The first release note.
  • The two numbers in §5 — ten working days and the monthly decision.

Pepper UI v1.5.0

The Mobupps design system. 386 tokens, 88 components, 65 pages in the workshop — every page of this site generated from the repository, so nothing here is typed twice.