# Harness Design Web

> Token-bound web component generation. Scaffold from design tokens and aesthetic intent, implement with Tailwind/CSS and React/Vue/Svelte patterns, and verify every value references the token set — no hardcoded colors, fonts, or spacing.

## When to Use

- Generating new web components that must conform to the project's design system tokens
- When `on_new_feature` triggers fire with web UI scope requiring token-bound component generation
- When `on_commit` triggers fire and new components contain hardcoded design values that should reference tokens
- Implementing design intent from `design-system/DESIGN.md` into concrete Tailwind classes, CSS custom properties, or CSS-in-JS theme values
- Converting mockups or wireframes into token-bound component code for React, Vue, or Svelte
- NOT for generating design tokens themselves (use harness-design-system)
- NOT for establishing aesthetic direction or anti-patterns (use harness-design)
- NOT for accessibility auditing (use harness-accessibility)
- NOT for mobile platform components (use harness-design-mobile)

## Process

### Phase 1: SCAFFOLD — Read Tokens, Detect Framework, Plan Structure

1. **Read design tokens.** Load `design-system/tokens.json` (W3C DTCG format). Extract:
   - Color tokens: primary, secondary, accent, neutral ramps, semantic colors (success, warning, error, info)
   - Typography tokens: heading and body font families, font weights, font sizes, line heights
   - Spacing tokens: spacing scale values (xs through 2xl or equivalent)
   - If `design-system/tokens.json` does not exist, stop and instruct the user to run `harness-design-system` first.

2. **Read design intent.** Load `design-system/DESIGN.md` for:
   - Aesthetic direction (style, tone, differentiator) from harness-design output
   - Anti-patterns to avoid (e.g., "no gradients on data elements," "no decorative borders")
   - Platform-specific web notes (CSS strategy preferences, animation guidelines, responsive behavior)
   - If `design-system/DESIGN.md` does not exist, warn the user and proceed with tokens only. Recommend running `harness-design` for richer output.

3. **Check harness configuration.** Read `harness.config.json` for:
   - `design.strictness` — enforcement level (`strict`, `standard`, `permissive`). Default to `standard`.
   - `design.platforms` — confirm `web` is in the platforms list.
   - `design.tokenPath` — custom token path (default: `design-system/tokens.json`).

4. **Detect web framework.** Scan the project for:
   - **React:** `package.json` contains `react` or `next` dependency, `.tsx`/`.jsx` files exist
   - **Vue:** `package.json` contains `vue` or `nuxt` dependency, `.vue` files exist
   - **Svelte:** `package.json` contains `svelte` or `@sveltejs/kit`, `.svelte` files exist
   - **Vanilla:** No framework detected, output plain HTML/CSS
   - If the user specified `--framework`, use that override.

5. **Detect CSS strategy.** Scan for:
   - **Tailwind:** `tailwind.config.*` exists, `@tailwind` directives in CSS, `class=` with Tailwind utility patterns
   - **CSS Modules:** `.module.css` or `.module.scss` files exist
   - **CSS-in-JS:** `styled-components`, `@emotion/styled`, `stitches` imports detected
   - **Vanilla CSS:** Global `.css`/`.scss` files with no module or utility pattern
   - If the user specified `--cssStrategy`, use that override.

6. **Load platform-specific web rules.** Read `agents/skills/shared/design-knowledge/platform-rules/web.yaml` for web-specific design rules including responsive breakpoints, browser compatibility considerations, and CSS best practices.

7. **Load anti-pattern definitions.** Read anti-pattern files from `agents/skills/shared/design-knowledge/anti-patterns/`:
   - `typography.yaml` — typographic anti-patterns (too many fonts, inconsistent scales)
   - `color.yaml` — color anti-patterns (hardcoded hex, insufficient contrast)
   - `layout.yaml` — layout anti-patterns (magic numbers, inconsistent spacing)
   - `motion.yaml` — motion anti-patterns (excessive animation, missing reduced-motion)

8. **Build token-to-CSS mapping.** Create a lookup table that maps each token to its CSS representation based on the detected strategy:
   - **Tailwind:** `color.primary.500` maps to `text-primary-500` / `bg-primary-500` (requires `tailwind.config` theme extension)
   - **CSS custom properties:** `color.primary.500` maps to `var(--color-primary-500)`
   - **CSS-in-JS:** `color.primary.500` maps to `theme.color.primary[500]`

9. **Plan component structure.** For the requested component(s), define:
   - Component file path(s) following project conventions
   - Props interface / type definition
   - Which tokens will be consumed (colors, typography, spacing)
   - Responsive behavior (breakpoints, layout changes)
   - Present the scaffold plan to the user before proceeding.

### Phase 2: IMPLEMENT — Generate Token-Bound Components

1. **Generate framework-specific component code.** Based on detected framework:

   **React (TSX):**
   - Functional component with TypeScript props interface
   - All color values reference tokens (via Tailwind classes, CSS variables, or theme object)
   - All typography values reference tokens (font family, weight, size from token set)
   - All spacing values reference tokens (padding, margin, gap from spacing scale)
   - Export component with display name

   **Vue (SFC):**
   - Single File Component with `<script setup lang="ts">`
   - Props defined with `defineProps` and TypeScript interface
   - Scoped styles reference CSS custom properties mapped from tokens
   - Template uses token-derived classes or inline styles via CSS variables

   **Svelte:**
   - Component with TypeScript `<script lang="ts">`
   - Props exported with `export let` declarations
   - Styles reference CSS custom properties mapped from tokens
   - Reactive declarations for computed style values

   **Vanilla HTML/CSS:**
   - Semantic HTML structure
   - CSS custom properties for all design values
   - No inline styles with hardcoded values

2. **Generate CSS strategy artifacts.** Based on detected CSS strategy:

   **Tailwind:**
   - Extend `tailwind.config` theme with token values (if not already present)
   - Map tokens to Tailwind utility classes in components
   - Use `@apply` sparingly — prefer utility classes in markup
   - Generate token-to-Tailwind mapping comment block at top of component

   **CSS Custom Properties:**
   - Generate `:root` declarations for all consumed tokens
   - Components reference `var(--token-name)` exclusively
   - No hardcoded hex, rgb, hsl, or font values in component styles

   **CSS-in-JS:**
   - Generate theme object from tokens
   - Components consume theme via provider/hook pattern
   - All style values reference theme properties

3. **Apply design intent constraints.** For each generated component:
   - Check against anti-patterns from `design-system/DESIGN.md` and `agents/skills/shared/design-knowledge/anti-patterns/`
   - Enforce style guidelines (e.g., minimal style means no decorative effects)
   - Apply tone-appropriate color usage (e.g., cool tone means no warm accents in neutral UI)
   - Respect platform-specific web notes (animation preferences, responsive behavior)

4. **Add USES_TOKEN annotations.** Insert comments in generated code documenting which tokens are consumed:
   ```
   /* @design-token color.primary.500 — primary action background */
   /* @design-token typography.heading.fontFamily — section heading */
   /* @design-token spacing.md — card internal padding */
   ```
   These annotations enable the knowledge graph to create `USES_TOKEN` edges from this component to the consumed `DesignToken` nodes.

### Phase 3: VERIFY — Check Token Binding and Design Constraints

1. **Scan for hardcoded values.** Use Grep to search generated files for:
   - Hardcoded color values: hex (`#[0-9a-fA-F]{3,8}`), `rgb()`, `hsl()`, named colors
   - Hardcoded font families: `font-family:` declarations not referencing tokens
   - Hardcoded spacing: pixel/rem values in margin/padding/gap not from the token scale
   - Each finding is a violation: the component uses a value not bound to a token.

2. **Verify token coverage.** For every design value in the generated component:
   - Confirm it resolves to a token in `design-system/tokens.json`
   - Confirm the token path is valid (e.g., `color.primary.500` exists in the token tree)
   - Report orphan references (token annotations pointing to non-existent tokens)

3. **Check anti-pattern compliance.** Cross-reference generated code against anti-patterns declared in `design-system/DESIGN.md` and the anti-pattern definitions in `agents/skills/shared/design-knowledge/anti-patterns/`:
   - Grep for patterns matching each declared anti-pattern
   - Flag violations with file path, line number, and the specific anti-pattern violated

4. **Query the knowledge graph.** If a graph exists at `.harness/graph/`:
   - Use `DesignIngestor` to verify `DesignToken` nodes exist for all referenced tokens
   - Verify `PLATFORM_BINDING` edges exist for web platform tokens
   - Use `DesignConstraintAdapter` to check for `VIOLATES_DESIGN` edges
   - Report any constraint violations with severity based on `design.strictness`

5. **Assign severity based on `designStrictness`:**
   - `permissive` — all findings are `info`
   - `standard` — hardcoded values and anti-pattern violations are `warn`, accessibility violations are `error`
   - `strict` — hardcoded values are `error` (blocks), anti-pattern violations are `warn`, accessibility violations are `error`

6. **Report verification results.** Present:

   ```
   WEB-001 [warn] Hardcoded color #3b82f6 — should reference token "color.primary.500"
     File: src/components/Button.tsx:12
     Fix: Replace with Tailwind class "bg-primary-500" or var(--color-primary-500)

   WEB-002 [info] Anti-pattern: gradient on card background
     File: src/components/MetricCard.tsx:28
     Fix: Use solid background from token "color.neutral.50"
   ```

7. **Run `harness validate`.** After verification, run project-level validation to confirm the new components integrate cleanly.

## Harness Integration

- **`harness validate`** — Run after generating components to verify project health. New files must not break existing constraints.
- **`harness scan`** — Run after component generation to update the knowledge graph with new `USES_TOKEN` edges from generated components to consumed tokens.
- **`DesignIngestor`** (`packages/graph/src/ingest/DesignIngestor.ts`) — Verifies that `DesignToken` nodes exist for all tokens referenced by generated components. If tokens are missing from the graph, run `harness scan` to re-ingest.
- **`DesignConstraintAdapter`** (`packages/graph/src/constraints/DesignConstraintAdapter.ts`) — Checks for `VIOLATES_DESIGN` edges during the VERIFY phase. Reports constraint violations at the configured strictness level.
- **`harness-design-system`** — Dependency. This skill reads tokens generated by harness-design-system. If tokens do not exist, instruct the user to run harness-design-system first.
- **`harness-design`** — Dependency. This skill reads aesthetic intent from harness-design output (`design-system/DESIGN.md`). Anti-pattern enforcement and style constraints come from the design intent document.
- **`harness-impact-analysis`** — When tokens change, impact analysis traces which components consume affected tokens via `USES_TOKEN` edges. Use this to identify components that need regeneration after token updates.

**Graph naming convention:** This skill uses PascalCase for node types (`DesignToken`, `DesignConstraint`) and UPPER_SNAKE for edge types (`USES_TOKEN`, `PLATFORM_BINDING`, `VIOLATES_DESIGN`) as conceptual labels. The graph schema registers these as snake_case identifiers (`design_token`, `design_constraint`, `uses_token`, `platform_binding`, `violates_design`). The adapter classes (`DesignIngestor`, `DesignConstraintAdapter`) handle the mapping — always use the adapters rather than constructing graph queries with raw type names.

## Success Criteria

- Generated components reference design tokens exclusively — no hardcoded color, font, or spacing values
- Framework detection correctly identifies React, Vue, Svelte, or vanilla projects
- CSS strategy detection correctly identifies Tailwind, CSS Modules, CSS-in-JS, or vanilla CSS
- Token-to-CSS mapping produces correct output for each CSS strategy (Tailwind classes, CSS custom properties, theme object references)
- `@design-token` annotations are present for every consumed token
- Anti-pattern compliance check catches violations declared in `design-system/DESIGN.md`
- Verification report uses severity levels matching `design.strictness` configuration
- `harness validate` passes after component generation

## Examples

### Example: React + Tailwind Button Component

**Context:** SaaS dashboard project. React with Tailwind CSS. Tokens generated by harness-design-system. Design intent established by harness-design (minimal style, cool professional tone).

**SCAFFOLD output:**

```
Framework detected:   React (Next.js)
CSS strategy:         Tailwind CSS (tailwind.config.ts)
Tokens loaded:        42 tokens from design-system/tokens.json
Design intent:        Minimal, cool professional (from DESIGN.md)
Component plan:       Button with primary/secondary/ghost variants
Token consumption:    color.primary.*, color.neutral.*, typography.body, spacing.sm/md
```

**IMPLEMENT output (Button.tsx):**

```tsx
/* @design-token color.primary.500 — primary variant background */
/* @design-token color.primary.700 — primary variant hover */
/* @design-token color.neutral.50 — ghost variant background */
/* @design-token color.neutral.900 — ghost variant text */
/* @design-token typography.body.fontFamily — button label */
/* @design-token typography.body.fontWeight — button label weight */
/* @design-token spacing.sm — button horizontal padding */
/* @design-token spacing.xs — button vertical padding */

import type { ButtonHTMLAttributes } from 'react';

interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: 'primary' | 'secondary' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
}

export function Button({
  variant = 'primary',
  size = 'md',
  className = '',
  children,
  ...props
}: ButtonProps) {
  const baseClasses =
    'inline-flex items-center justify-center font-body font-medium rounded-md transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500';

  const variantClasses = {
    primary: 'bg-primary-500 text-white hover:bg-primary-700',
    secondary: 'border border-primary-500 text-primary-700 hover:bg-primary-50',
    ghost: 'text-neutral-900 hover:bg-neutral-100',
  };

  const sizeClasses = {
    sm: 'px-spacing-sm py-spacing-xs text-sm',
    md: 'px-spacing-md py-spacing-sm text-base',
    lg: 'px-spacing-lg py-spacing-md text-lg',
  };

  return (
    <button
      className={`${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]} ${className}`}
      {...props}
    >
      {children}
    </button>
  );
}
```

**VERIFY output:**

```
Verification Results
--------------------
Hardcoded values:    PASS (0 hardcoded colors, fonts, or spacing)
Token coverage:      PASS (8/8 referenced tokens exist in tokens.json)
Anti-pattern check:  PASS (no gradients, no decorative borders)
Design constraints:  PASS (no VIOLATES_DESIGN edges)
Harness validate:    PASS
```

### Example: Vue + CSS Custom Properties Card Component

**Context:** E-commerce project. Vue 3 with CSS custom properties. Tokens from harness-design-system.

**IMPLEMENT output (ProductCard.vue):**

```vue
<!-- @design-token color.neutral.50 — card background -->
<!-- @design-token color.neutral.200 — card border -->
<!-- @design-token color.neutral.900 — card text -->
<!-- @design-token typography.heading.fontFamily — product title -->
<!-- @design-token typography.body.fontFamily — product description -->
<!-- @design-token spacing.md — card padding -->
<!-- @design-token spacing.sm — content gap -->

<script setup lang="ts">
interface Props {
  title: string;
  description: string;
  price: string;
  imageUrl: string;
  imageAlt: string;
}

defineProps<Props>();
</script>

<template>
  <article class="product-card">
    <img :src="imageUrl" :alt="imageAlt" class="product-card__image" />
    <div class="product-card__content">
      <h3 class="product-card__title">{{ title }}</h3>
      <p class="product-card__description">{{ description }}</p>
      <span class="product-card__price">{{ price }}</span>
    </div>
  </article>
</template>

<style scoped>
.product-card {
  background: var(--color-neutral-50);
  border: 1px solid var(--color-neutral-200);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.product-card__content {
  padding: var(--spacing-md);
  display: flex;
  flex-direction: column;
  gap: var(--spacing-sm);
}

.product-card__title {
  font-family: var(--typography-heading-fontFamily);
  font-weight: var(--typography-heading-fontWeight);
  color: var(--color-neutral-900);
}

.product-card__description {
  font-family: var(--typography-body-fontFamily);
  color: var(--color-neutral-700);
}
</style>
```

## Rationalizations to Reject

| Rationalization                                                                                                                                | Reality                                                                                                                                                                                                                                                |
| ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| "The tokens file doesn't exist yet, but I know the brand colors — I'll hardcode them as a placeholder and note they should be replaced later." | Hardcoded values in generated output are the exact problem this skill exists to prevent. There is no placeholder exception. If `design-system/tokens.json` does not exist, instruct the user to run harness-design-system first and stop.              |
| "The framework is obviously React — everything in this project is React. I don't need to run detection."                                       | Detection also identifies the CSS strategy (Tailwind vs CSS Modules vs CSS-in-JS), which determines how tokens map to code. Skipping detection produces components that may reference non-existent Tailwind classes or wrong theme paths.              |
| "The user hasn't confirmed the scaffold plan, but the component structure is straightforward — I'll just generate it."                         | The scaffold plan confirmation is a gate. The user must see which tokens will be consumed and what the component structure will be before code is written. Generating first and explaining later inverts the review opportunity.                       |
| "This component only uses one hardcoded hex value for a shadow — that's not really a design value, so I'll leave it."                          | Every color, font, and spacing value must reference a token. Shadows use color tokens. "Not really a design value" is not a category the verification phase recognizes. The VERIFY phase will flag it; the IMPLEMENT phase should not introduce it.    |
| "The `@design-token` annotations are just comments — skipping them on a few components won't affect anything."                                 | These annotations are how `harness scan` creates `USES_TOKEN` edges in the knowledge graph. Missing annotations mean harness-impact-analysis cannot trace token changes to affected components. They are structural metadata, not decorative comments. |

## Gates

These are hard stops. Violating any gate means the process has broken down.

- **No component generation without reading tokens from harness-design-system.** The SCAFFOLD phase requires `design-system/tokens.json` to exist. If tokens have not been generated, instruct the user to run harness-design-system first. Do not generate components with hardcoded values as a fallback.
- **No hardcoded design values in generated output.** Every color, font, and spacing value must reference a token. If a needed token does not exist, instruct the user to add it via harness-design-system rather than hardcoding the value.
- **No framework-specific code without framework detection.** The SCAFFOLD phase must detect or receive the target framework before generating components. Do not guess or default to React.
- **No generation without scaffold plan confirmation.** The SCAFFOLD phase must present a component plan to the user. Do not generate code without the user confirming the structure and token consumption plan.
- **No graph mutations without validating node types.** When creating `USES_TOKEN` or `PLATFORM_BINDING` edges, verify the node and edge types are registered in the graph schema before writing.

## Escalation

- **When `design-system/tokens.json` does not exist:** Instruct the user: "Design tokens have not been generated. Run `harness-design-system` first to create `design-system/tokens.json`, then re-run `harness-design-web` for token-bound component generation."
- **When the project uses an undetected framework:** Ask the user to specify via the `--framework` CLI argument. Log: "Could not auto-detect web framework. Please specify: `harness skill run harness-design-web --framework react|vue|svelte|vanilla`."
- **When tokens are insufficient for the requested component:** Report which tokens are missing: "Component requires a `color.accent.500` token but it does not exist in `tokens.json`. Run `harness-design-system` to add the missing token, or choose an existing alternative." Do not hardcode a fallback.
- **When anti-patterns conflict with the requested design:** Present the conflict: "The requested gradient background violates the 'no gradients on data elements' anti-pattern from DESIGN.md. Options: (1) Remove the gradient and use a solid token color, (2) Update the anti-pattern list via `harness-design` if the intent has changed."
- **When the knowledge graph is unavailable:** Skip graph operations in SCAFFOLD and VERIFY phases. Log: "Graph not available at `.harness/graph/` — skipping token node verification and USES_TOKEN edge creation. Run `harness scan` later to populate." Continue with file-based operations.
