Agentic Development
The core idea
Section titled “The core idea”Most design systems describe how to build components. BPL DS defines what components are.
The distinction matters for agents. An implementation guide requires the agent to understand intent and translate it into code. A definition is the code — the agent reads it, generates conforming output, and another agent can verify that output against the same source.
BPL DS has three layers of definitions:
Tokens → CSS custom properties on :root — the visual languageComponents → HTML shape + public CSS API — the structural contractState → ARIA attributes + native pseudo-classes — the behavioral contractNo framework, no build pipeline, no translation. The definition is the implementation.
How the contract works
Section titled “How the contract works”Every component in BPL DS is a contract with three parts.
1. HTML shape
Section titled “1. HTML shape”One canonical HTML structure. Documented on each component page. The agent copies it — it does not invent alternatives.
<!-- This IS the button contract --><button class="bp-btn" type="button">Label</button>The HTML uses the correct semantic element, exposes the correct ARIA surface, and carries the correct BEM class structure. Deviation from the documented shape is a contract violation.
2. Public CSS API — Token Contract
Section titled “2. Public CSS API — Token Contract”Every component exposes customization via CSS custom properties. These are the public API — the only supported surface for customization.
The DS uses a 4-level variable chain:
| Level | Example | Role |
|---|---|---|
| 0 — Figma | --figma-blue-500 | Raw palette from Style Dictionary. Never used in CSS directly. |
| 1 — DS base | --bp-primary | DS tokens on :root. Client remaps these to Figma values. |
| 2 — Component slot | --btn-background | Public override API. Named after the CSS property it controls. |
| 3 — Private | --_background | Internal resolver. Never set from outside the component. |
/* DS source */.bp-btn { --_background: var(--btn-background, var(--bp-primary)); background-color: var(--_background);}Rule 1 — Token names match the CSS property
Section titled “Rule 1 — Token names match the CSS property”--btn-background controls background-color. --btn-border-radius controls border-radius. No abbreviations (--btn-bg ❌).
Rule 2 — No state tokens
Section titled “Rule 2 — No state tokens”Re-declare the same Level 2 token in a state selector. Never create a separate --btn-background-hover token.
.my-cta { --btn-background: var(--bp-cta); }.my-cta:hover { --btn-background: var(--bp-cta-hover); }Rule 3 — Private vars use --_ prefix
Section titled “Rule 3 — Private vars use --_ prefix”Short property-named suffixes only: --_background, --_color, --_border-radius. Private vars live only on the component root selector.
Rule 4 — Component tokens never go on :root
Section titled “Rule 4 — Component tokens never go on :root”Global theming flows through --bp-*. Component tokens (--btn-*) are per-instance exception slots only.
/* ✅ Global theme — only touch Level 1 */:root { --bp-primary: var(--figma-blue-500); }
/* ✅ Per-instance exception */.hero__cta { --btn-background: var(--bp-color-accent); }
/* ❌ Component token on :root — affects all buttons, hard to override */:root { --btn-background: var(--figma-blue-500); }Rule 5 — Child element tokens only when documented
Section titled “Rule 5 — Child element tokens only when documented”Child styling is private by default. Expose a Level 2 slot for a child element only when there is a consumer need, following --<component>-<element>-<property>:
/* Only if documented in the Public API table */.bp-card { --_link-color: var(--card-link-color, var(--bp-primary)); }.bp-card a { color: var(--_link-color); }An agent customizing a button sets Level 2 tokens on a CSS class. It does not touch the component CSS source, does not add inline style="", and does not invent class names outside the BEM structure.
/* ✅ Correct — agent sets public API via a scoped class */.cta-button { --btn-background: var(--bp-color-success); --btn-border-radius: var(--bp-radius-full);}<!-- ✅ Correct — applies the modifier class to the component root --><button class="bp-btn cta-button" type="button">Get started</button>3. State contract
Section titled “3. State contract”Visual state lives in the DOM, not in JS-applied classes. CSS reads it. JS writes it.
DOM attribute / property → CSS reads it → visual changearia-selected="true" → .bp-tabs__tab[aria-selected="true"] { … }[hidden] → .bp-accordion__panel[hidden] { display: none }:checked → .bp-checkbox__input:checked + .bp-checkbox__label { … }:disabled → .bp-input:disabled { opacity: 0.5 }An agent adding interactivity sets ARIA attributes and native DOM properties. It does not toggle style classes. This is not a convention — it is the mechanism by which CSS reads state.
Agent workflow
Section titled “Agent workflow”Implementing a UI from a definition
Section titled “Implementing a UI from a definition”1. Read the component page on the docs site2. Copy the documented HTML — do not invent structure3. Identify the required public API properties from the "Public API" table4. If customization is needed: create a CSS class that sets the relevant --<component>-* variables5. Apply that class to the component root element6. For interactive components: copy the JS snippet from the "JavaScript" section verbatim7. Verify in browser — run `pnpm dev`, open the routeNo mock DOM, no test runner, no build step required to verify.
What agents can do without human input
Section titled “What agents can do without human input”- Compose layouts using
.bp-content-gridand its named zones (popout,breakout,full-width) - Implement any component by copying its documented HTML
- Customize appearance using the public CSS API
- Combine components: a carousel of cards, a modal containing a form, a nav with badge counts
- Apply theme tokens across a section using
data-theme - Add interactivity using the documented JS snippets for Custom Element components
What requires human judgment
Section titled “What requires human judgment”- Choosing between elements when semantic ambiguity exists (
<dl>vs<ul>for a definition-style list) - Deciding whether to adapt an existing component or build a new one
- Content decisions: heading levels,
aria-labeltext, accessible descriptions - Visual design decisions outside the token vocabulary
Self-review checklist
Section titled “Self-review checklist”Before marking UI work complete, verify each item.
- Used the documented HTML shape from the component page — no invented structure
- Used the correct semantic element (
<button>for actions,<a>for navigation,<dialog>for modals) - No class names outside the documented BEM structure
- No
style=""anywhere in HTML
- Customization uses
--<component>-*properties on a scoped CSS class - That class is applied to the component root element
- Tokens used are from the
--bp-*vocabulary — no magic values
- Visual state changes set ARIA attributes or native properties — not style classes
- JS does not apply or remove CSS classes for visual changes
JavaScript (if applicable)
Section titled “JavaScript (if applicable)”- Only uses documented APIs (
showModal(),close(),aria-selected, etc.) - Custom Elements loaded via
<script type="module" src="…">
Accessibility
Section titled “Accessibility”- Interactive elements are keyboard reachable (no
tabindexon non-interactive elements) - All images have meaningful
alttext (oralt=""if decorative) - Form inputs have an associated
<label> -
aria-label/aria-describedbyadded only when no visible label exists
Semantic
Section titled “Semantic”- Heading levels are sequential — no skipped levels
- Lists use
<ul>/<ol>/<dl>— not<div>chains - Landmark regions present:
<header>,<main>,<nav>,<footer> - No
<div>or<span>where a semantic element fits
Verification contract
Section titled “Verification contract”Because the implementation derives from a definition, verification is mechanical.
An agent reviewer can check conformance without subjective judgment:
| Rule | Verifiable by |
|---|---|
| HTML uses the documented element and class structure | String matching |
No style="" in HTML | AST / grep |
Customization uses --<component>-* properties in a CSS class | CSS parser |
| State changes set ARIA attributes, not style classes | DOM inspection |
JS only calls documented APIs (showModal(), aria-selected, etc.) | Code review |
Tokens used are from the defined vocabulary (--bp-*) | CSS custom property audit |
This mechanical verifiability is why BPL DS works well in agent pipelines. A second agent can review a first agent’s output against these rules with high confidence and low hallucination risk.
Agent configuration
Section titled “Agent configuration”CLAUDE.md
Section titled “CLAUDE.md”## BPL DS — Design system rules
This project uses BPL DS. Agents implementing UI must follow these rules exactly.
### HTML contract- Components are HTML + CSS. No React/Vue/Svelte wrappers from the DS.- Copy the HTML from https://ds.bepartnerlabs.com/components/<name>/ verbatim.- Use the correct semantic element: `<button>` for actions, `<a>` for navigation, `<dialog>` for modals.- Do not invent class names outside the documented BEM structure.
### CSS contract- Tokens are CSS custom properties defined in `src/styles/tokens.css` (`--bp-*`).- Customize components via their public API: CSS custom properties `--<component>-*` on a scoped class.- Never use `style=""` in HTML. Always a CSS class.- BEM modifiers for visual variants: `.bp-btn--danger`, `.demo-card--ghost`.
### State contract- Visual state: CSS reads ARIA attributes and native pseudo-classes. JS sets them.- JS is allowed for: `dialog.showModal()` / `dialog.close()`, setting `aria-selected` / `aria-expanded`, roving tabindex, keyboard navigation listeners.- Never apply style classes from JS.
### Custom Elements- `<bp-dropdown>`, `<bp-table>`, `<bp-toast>` are Custom Elements. Load with `<script type="module" src="…">`.- Without the script, the HTML renders but keyboard navigation is absent.
### Verification- `pnpm dev` → open in browser. No test runner required.- Check: documented HTML shape matches, no `style=""`, state via ARIA.Cursor / other agents (.cursorrules)
Section titled “Cursor / other agents (.cursorrules)”# BPL DS contract rules
Components: HTML + CSS classes. No framework wrappers.HTML: copy from docs. Do not invent class names.Customization: CSS custom properties (--<component>-*) in a stylesheet class. Never style="".State: CSS reads ARIA + native pseudo-classes. JS writes aria-selected, aria-expanded, hidden, open.Never: style classes from JS, inline styles, class names outside BEM structure.Semantic HTML: <button> actions, <a> navigation, <dialog> modals, <details> accordions.Why this architecture works for agents
Section titled “Why this architecture works for agents”No ambiguity in the API surface. The public CSS properties are documented. The HTML shape is documented. There is nothing to infer.
Errors are detectable. A deviation from the contract (style="" in HTML, a JS-toggled class, an undocumented class name) is a rule violation, not a style judgment. A reviewer can flag it without understanding the business logic.
The same definition serves all consumers. An agent building in React, a developer building in Rails, and a second agent reviewing the output all share the same source of truth. No framework-specific contract exists.
State is in the DOM, not in the agent’s memory. The agent does not need to track which tab is selected or whether a modal is open. The DOM holds that state in ARIA attributes, and CSS reads it. The agent’s job is to write the initial HTML correctly and wire the state changes to the right attributes.
Tokens make visual consistency emergent. An agent that uses --bp-space-4 for spacing and --bp-color-error for error states produces visually consistent output across the whole product without needing design judgment — because the judgment was encoded into the token definitions at design time.