Skip to content

Agentic Development

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 language
Components → HTML shape + public CSS API — the structural contract
State → ARIA attributes + native pseudo-classes — the behavioral contract

No framework, no build pipeline, no translation. The definition is the implementation.


Every component in BPL DS is a contract with three parts.

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.

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:

LevelExampleRole
0 — Figma--figma-blue-500Raw palette from Style Dictionary. Never used in CSS directly.
1 — DS base--bp-primaryDS tokens on :root. Client remaps these to Figma values.
2 — Component slot--btn-backgroundPublic override API. Named after the CSS property it controls.
3 — Private--_backgroundInternal 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 ❌).

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); }

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>

Visual state lives in the DOM, not in JS-applied classes. CSS reads it. JS writes it.

DOM attribute / property → CSS reads it → visual change
aria-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.


1. Read the component page on the docs site
2. Copy the documented HTML — do not invent structure
3. Identify the required public API properties from the "Public API" table
4. If customization is needed: create a CSS class that sets the relevant --<component>-* variables
5. Apply that class to the component root element
6. For interactive components: copy the JS snippet from the "JavaScript" section verbatim
7. Verify in browser — run `pnpm dev`, open the route

No mock DOM, no test runner, no build step required to verify.

  • Compose layouts using .bp-content-grid and 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
  • 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-label text, accessible descriptions
  • Visual design decisions outside the token vocabulary

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
  • Only uses documented APIs (showModal(), close(), aria-selected, etc.)
  • Custom Elements loaded via <script type="module" src="…">
  • Interactive elements are keyboard reachable (no tabindex on non-interactive elements)
  • All images have meaningful alt text (or alt="" if decorative)
  • Form inputs have an associated <label>
  • aria-label / aria-describedby added only when no visible label exists
  • 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

Because the implementation derives from a definition, verification is mechanical.

An agent reviewer can check conformance without subjective judgment:

RuleVerifiable by
HTML uses the documented element and class structureString matching
No style="" in HTMLAST / grep
Customization uses --<component>-* properties in a CSS classCSS parser
State changes set ARIA attributes, not style classesDOM 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.


## 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.
# 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.

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.