Crafting Component API, Together
How to unify anatomy and props across code and design tools
A design system aspires to achieve a shared vocabulary between designers and developers. As we build visual style and UI components, we make many decisions about how a feature is composed and configured.
- What is its name?
- How is each element organized and named in a hierarchy?
- What options can be configured, and at what level is each exposed?
- How might smaller parts be modularized to reuse elsewhere?
- How does this thing interact with other things that call or contain it?
Answers lead to a formal Application Programming Interface (API) to establish how to control how a feature works. During these conversations, practitioners often bemoan “naming is hard.” Backchannel messages evoke emoji like 😩, 🙄, and 😤 . Nevertheless, such decisions, models and conventions are a big part of what makes design systems work.
API has long been the developer’s domain. Yet, if design systems deliver a shared vocabulary across libraries built in and documented for developers and designers, shouldn’t a API be as similar as possible in code and design tools? This is hard when teammates have divergent mental models and distinct mediums that (decreasingly?) constrain them to build differently.
Design tools have long enabled composition and configuration of components in a manner different than code. Designers have tinkered with API in design tools for years, although they haven’t called it API. Their practice has been crude, disconnected. Sketch’s symbol overrides come to mind, forcing odd properties and wacky layers so distinct from how code works. Results weren’t great, only went so far, and enabled a convenient, lazy excuse to avoid trying to improve a shared language. “Why bother,” they’d claim, “if half measures don’t help translate choices efficiently from one tool to another?” I get it, except for the lazy part.
The mood shifts. Design systems are doing much better in sharing an API across design and dev, due in part to rapid advances in design tools. For example, Figma’s Variants opened minds to concretely mirror how code works in designer tools. To realize a shared API, cross-disciplinary teams must identify what to include, when to fit API into a workflow, where to author together, and how behaviors change as a result.
What to include in component API drafts
It’s unrealistic to think an API will be robust, stable and complete before coding starts. However, my experience suggests there’s plenty a team can normalize early. We’ve found success focusing on three topics: anatomy, properties, and layout.
Anatomy establishes the hierarchy of elements and groups that map to web markup, object composition, and Figma layers. Anatomy should also reveal needed subcomponents likely requiring their own properties as well as dependencies on other more atomic things.
-----------<Card metadata="" title="">
<CardMedia> (extends <Image>)
<CardDescription> (slot) </CardDescription>
<CardActionsArea> (slot to add Button, IconButton, or TextLink) </CardActionsArea>
For example, a developer sketching a modular Card component may rough out a structure where some elements (such as
title) are handled by properties, and other elements (such as
CardActionsArea) are composible.
]-[ .Card (as Base Figma component)
CardImage (extends Image as subcomponent)
On the other hand, a designer mapping an anatomy to Figma layers may outline a similar yet different anatomy. Some intent is Figma specific, such as a “tie fighter” (
]-[) or “inset” (
[-]) to anticipate auto-layout spacing or prepending a
. for a base component or subcomponent.
Yet, these rapid drafts expose divergence both realistic and avoidable. Differences include element names (
description), hierarchy (are
actions contained by or a sibling of
content?), and subcomponents (
Both developer and design tools should evoke consistent property names, option names, and defaults.
Figma only:- State
- hover? (recommend: include)
- focus? (recommend: include)
- errorHover? (recommend: omit)
- readOnlyBoth Figma & code:- required (false - default, true)
- inlineLabel (false - default, true)
- helperTextPlacement (right, bottom - default)Code only:- ariaLabel
- disabled (in Figma, use States)
- error (in Figma, use States)
- errorText (in Figma, update text shown when in State=Error)
- helperText (in Figma, show/hide and update text)
- label (use .Label element)
- readOnly (false - default, true) (in Figma, use States)
For example, both tools can evoke the same Dropdown properties for
inlineLabel (a boolean toggle) and
helperTextPlacement (two named options:
bottom). On the other hand, a coded component may enable some states via properties and other are triggered only via interaction. In that case, it may be desirable to consolidate both property- and interaction-triggered states in a single menu for designers in Figma while sustaining consistent option labels. Finally, it’s very common for code to include (many?) more properties, such as
While less tangible than anatomy and properties, it’s important to relate those choices to element-by-element width, height, spacing, fluidity, and responsive breakpoints.
- Desktop: 1440px
- Desktop/Tablet: 1024px
- Tablet: 768px
- Mobile: 368px
Admittedly, these notes are less structured and vary considerably across components. It’s been tough to establish a template for drafting them. However, we’ve always found it useful to run through the anatomy, element by element and group by group, to discuss and take notes on how items are contained and fluidly lay out under varying conditions across tools.
When to fit API into a design system workflow
Since 2016, EightShapes has evolved how we deliver features with our clients starting from two core steps: designers deliver Design to developers that Code. The process has expanded over years, adding steps and refining substeps to Plan, Spec (the approved design), Document (design guidelines), produce Design Assets (such as Figma variants) reused by other designers, and Release the feature.
A few years ago, we recognized a transitional moment some teams call “The Handoff” when the linear path of “design” diverges to produce many things. This moment sets in motion designer(s) and developer(s) down what can be increasingly isolated tasks in different tools, sometimes across platforms (like web, iOS, and Android). In the design system I currently lead currently, a designer hands off the same design spec to five distinct people that produce outputs in different places! As a result, it’s critical to normalize an API before individuals get too far into production.
For small teams: an activity within task(s)
For some small teams, normalizing an API can be done as one collaborating designer and one collaborating developer sync early in production. We’ve had success assigning this work to a developer to draft a proposal, the whole squad critiques, and the assigned designer speaks to impacts to design tools and documentation.
For many makers and/or outputs: a dedicated, formal task
On the other hand, precede production with a formal API step when handing off to 2+ developers or designers. This task shouldn’t take long, and is done when “producers have discussed shared concerns and agreed on a provisional API direction across outputs.” Sure, some designers or developers may be eager to get started before the API task is done. If so, each must acknowledge refactoring may be needed as consensus on the API comes into focus.
Starting from what exists: evaluate during planning
Starting from an existing catalog like Material UI has become quite a theme (pun intended!). Evaluating an existing API when Planning feature work is essential. Here, the conversation isn’t just to align developer and designer outputs, but also to identify constraints the team must live within or break away from in the adopted library.
Similarly, another team migrating from Sketch to Figma used a Figma API step to evaluate existing design specs, existing Sketch symbols, and — by far most importantly — code that already exists. Injecting the step enabled each designer to involve developers to review and confirm before building. Was it painstaking? It felt that way at first, but saved considerable time and improved alignment as the team canvased the catalog.
Where to draft an API
The developer assigned to code a component often takes the first pass at an API. On the other hand, I’m an architect by trade and find myself constantly drafting API and coaching industrious front-end aware designers to do the same. No matter who starts the exercise, an API draft should be:
…composed in a visible tool. The draft must be findable and editable by everyone. For some teams, Google Docs or Office365 suffices. Other teams favor Jira or Asana. Discipline-specific tools — a code file or a Figma page—have proven less cross-functionally inclusive.
…started from a template. Includes familiar structure, implies conventions (such as how to list property options), and embed hints.
…critiqued as a group. Once drafted, it’s healthy to critique an API as a multi-disciplinary team. Conversations last anywhere from 15 minutes to an hour per component, based on complexity. Expect to work through a few sessions before team members get the hang of it.
…available for asynchronous review. Not everyone attends every discussion, yet every downstream assignee should an opportunity to comment on a proposed API. For features serving a wide shared need, a core team may also invite the design and/or development community to review, often via a request for comments via Slack or Teams.
Epilogue: How crafting API changes behaviors
Teams using this approach gradually acclimate to composing an API together. Divergence does occur as teams learn by making. Teams effectively aligning API across outputs exhibit behaviors like:
- Developers invite designers to collaborate on properties and anatomy, rather than gatekeeping something historically their domain.
- Design specs drift toward API constructs, with sections of Anatomy and “Variants” (organizing some properties) grounding handoff.
- Developers and designers naturally connect as outputs evolve. Coding reveals technical constraints. Design spec deficiencies arise. Designers in Figma hit walls that can’t quite mimic how code works. Let’s talk about it.
This reinforces the reality that drafting API early isn’t (just) to minimize divergence. It’s also to anchor collaborative expectations and behaviors between designers and developers via a shared starting point. Happy APIing, you lovely cross-functional teams!