Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
2b60578
add react code connect config
R-Bower Jan 4, 2026
370f5b7
add checkbox, simplify figma file url
R-Bower Jan 4, 2026
c8d51f9
fix(button): apply icon styles to child lucide-icon elements
R-Bower Jan 5, 2026
6d47376
add foundation button figma, add icon generation / fetch cli
R-Bower Jan 5, 2026
4bbf1bd
feat(link): add brand and white-persistent colors
R-Bower Jan 5, 2026
cb13d6a
docs: update link color demos
R-Bower Jan 5, 2026
1ff51c4
add link figma code connect
R-Bower Jan 5, 2026
104ebdf
fix(icons): apply proper width/height to svg children
R-Bower Jan 5, 2026
b8989b0
add text input code connect
R-Bower Jan 5, 2026
99514db
feat: radio
olaf-k Jan 5, 2026
4e1b405
feat: accordion
olaf-k Jan 7, 2026
8966fa3
feat: inline notification
olaf-k Jan 8, 2026
c60c7e4
chore: Claude agent for Code Connect
olaf-k Jan 8, 2026
5fe6176
feat: avatar
olaf-k Jan 8, 2026
bb89218
feat: badge
olaf-k Jan 8, 2026
26fd9fc
feat: number input
olaf-k Jan 8, 2026
adbe87c
feat: password input
olaf-k Jan 8, 2026
1d02314
feat: switch
olaf-k Jan 9, 2026
41248cb
feat: text area
olaf-k Jan 9, 2026
273f0c7
Update code-connect-generator.md
olaf-k Jan 9, 2026
db09681
feat: divider
olaf-k Jan 9, 2026
323a069
feat: header bar
olaf-k Jan 9, 2026
8efecd4
feat: segmented control
olaf-k Jan 9, 2026
fd407a0
feat: tag
olaf-k Jan 9, 2026
134acc6
Merge branch 'main' into feature/figma-code-connect
R-Bower Jan 12, 2026
231dd4a
use separate branch
R-Bower Jan 12, 2026
a885c99
add tooltip code connect
R-Bower Jan 12, 2026
7904ffc
testing angular integration
R-Bower Jan 12, 2026
b344aae
Merge branch 'feature/figma-code-connect' into olaf/code-connect
olaf-k Jan 12, 2026
66d1f93
feat: breadcrumbs
olaf-k Jan 12, 2026
3085563
feat: pagination
olaf-k Jan 12, 2026
ca31996
feat: side-nav
olaf-k Jan 12, 2026
d2d8e8d
feat: tabs
olaf-k Jan 12, 2026
84af144
chore: update agent
olaf-k Jan 12, 2026
9ba54c1
feat: progress (bar)
olaf-k Jan 13, 2026
f743192
feat: progress (ring)
olaf-k Jan 13, 2026
75b0bf4
feat: toast notification
olaf-k Jan 13, 2026
4c7ee18
feat: select
olaf-k Jan 14, 2026
e6dfbb1
feat: tree
olaf-k Jan 14, 2026
41c9704
feat: slider
olaf-k Jan 14, 2026
1a1f9fc
feat: combobox
olaf-k Jan 14, 2026
ba9e5b5
feat: dialog
olaf-k Jan 15, 2026
40cf2a7
feat: drawer
olaf-k Jan 15, 2026
aca29eb
feat: popover
olaf-k Jan 15, 2026
aca8c0b
feat: menu
olaf-k Jan 15, 2026
3bab55b
Merge branch 'main' into olaf/code-connect
R-Bower Jan 18, 2026
a226b5e
fix ts
R-Bower Jan 18, 2026
2ac2ee0
exclude figma files from react build
R-Bower Jan 18, 2026
a1cbc09
type fix
R-Bower Jan 18, 2026
8cdde60
Merge branch 'main' into olaf/code-connect
olaf-k Jan 29, 2026
d957bae
Update pnpm-lock.yaml
olaf-k Jan 29, 2026
d89a859
fix: tag URL + dialog update
olaf-k Jan 29, 2026
1043f65
Merge branch 'main' into olaf/code-connect
olaf-k Jan 30, 2026
fc447f2
skip lint
olaf-k Jan 30, 2026
2e17dcf
license
olaf-k Jan 30, 2026
c0d1c3b
fix(tag): node ids etc
olaf-k Jan 30, 2026
349862c
Merge branch 'main' into olaf/code-connect
olaf-k Jan 30, 2026
905255c
fix: checkbox (helper) switch (helper/size)
olaf-k Jan 30, 2026
0197779
fix(radio): hint + invalid
olaf-k Jan 30, 2026
c348db4
feat(number-input): unit selector
olaf-k Jan 30, 2026
94b5cf3
fix(avatar): node ids + main example
olaf-k Feb 2, 2026
030907c
fix(header bar): padding
olaf-k Feb 2, 2026
9436aad
fix(password input): placeholder
olaf-k Feb 2, 2026
39baf51
fix(popover): emphasis
olaf-k Feb 2, 2026
d6e5564
fix(progress): no disabled
olaf-k Feb 2, 2026
b9ebb5a
fix(slider): error text
olaf-k Feb 2, 2026
1d4c87e
fix: text-input
olaf-k Feb 2, 2026
d5d6e85
fix(toast): closable + info
olaf-k Feb 2, 2026
4f02b13
feat: button-group
olaf-k Feb 6, 2026
e504ef9
fix: progress/bar
olaf-k Feb 10, 2026
25cce31
rename button-foundation
olaf-k Feb 10, 2026
ba7c265
Merge branch 'main' into olaf/code-connect
R-Bower Feb 12, 2026
d953072
figma cleanup, re-enabled eslint
R-Bower Feb 13, 2026
e7918d9
add figma publish workflow, update figma config targets
R-Bower Feb 13, 2026
7b0173f
revert button sizes demo, add link changesets
R-Bower Feb 13, 2026
fad8ca7
button update
olaf-k Feb 13, 2026
b6636d9
Update avatar.figma.tsx
olaf-k Feb 13, 2026
ae53e54
version bumps, changelogs
R-Bower Feb 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .changeset/forty-mammals-find.md

This file was deleted.

242 changes: 242 additions & 0 deletions .claude/agents/code-connect-generator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
---
name: code-connect-generator
description: |
Generates Figma Code Connect files for components in the @qualcomm-ui repository. Invoke when the user needs Code Connect configurations created or updated for Figma design system integration.
model: inherit
color: blue
---

You are a Figma Code Connect specialist for the @qualcomm-ui design system. Your job is to create accurate Code Connect configuration files that map Figma components to their code implementations.

## Critical Rules

### 1. Verify Figma Properties Before Using Them

**Always use Figma MCP tools** (`get_design_context`, `get_metadata`) to verify property names exist. Never assume.

- Property names vary between similar components (e.g., `inputText` vs `passwordText`)
- If a property doesn't exist but is needed, hardcode it conditionally

```tsx
// ❌ WRONG - assumed property exists
errorText: figma.string("errorText")

// ✓ CORRECT - property doesn't exist, hardcode for relevant states
errorText: figma.enum("state", {
invalid: "Error message",
"invalid-focus": "Error message",
})

// ✓ CORRECT - hardcode icon when instance mapping is impractical
startIcon: figma.boolean("startIcon", {
true: "KeyRound",
})
```

### 2. Never Include Default Values

Before writing any `figma.enum()`, identify the component's default and omit it:

```tsx
// If "md" is the default size:

// ❌ WRONG
size: figma.enum("size", { lg: "lg", md: "md", sm: "sm" })

// ✓ CORRECT
size: figma.enum("size", { lg: "lg", sm: "sm" })
```

### 3. Use Uncontrolled Props for Form Components

Code Connect examples are static snapshots. Use uncontrolled variants (`default*` props) instead of controlled ones:

```tsx
// ❌ WRONG - controlled prop implies state management
checked: figma.enum("variant", { checked: true })

// ✓ CORRECT - uncontrolled prop for static example
defaultChecked: figma.enum("variant", { checked: true })
```

Common mappings:
- `checked` → `defaultChecked`
- `value` → `defaultValue`
- `selected` → `defaultSelected`
- `pageSize` → `defaultPageSize`
- `page` → `defaultPage`

### 4. Code Connect Files Are Templates, Not TypeScript

The parser extracts text patterns - no runtime JS/TS features work:

```tsx
// ❌ WRONG - these will fail
status={showStatus ? "active" : undefined}
{showStatus && <Avatar.Status />}
true: "active" as const

// ✓ CORRECT - handle conditionals in props definition
status: figma.boolean("status", { true: "active" })
statusIndicator: figma.boolean("status", { true: <Avatar.Status /> })
```

### 5. Derive Multiple Props from One Figma Property

When Figma shows a combined visual but React needs multiple props to achieve it, use the same Figma property to derive both:

```tsx
// Figma shows "0/100" when count=true
// React needs both `counter` AND `maxLength` to display "n/max"

counter: figma.boolean("count"),
maxLength: figma.boolean("count", {
true: 100,
}),
```

### 6. Ignore Display-Only Figma Properties

Some Figma properties exist purely for design preview and have no React equivalent because the component auto-generates that content. Do not map these.

Examples:
- `countText` ("0/100") - React's counter auto-generates this from `maxLength`
- `inputText` when used only for visual preview - map via `defaultValue` instead

### 7. No Figma Helpers Inside JSX Children

You cannot interpolate figma helpers as children inside JSX elements:

```tsx
// ❌ WRONG - figma.string() inside JSX children doesn't work
appTitle: figma.boolean("showTitle", {
true: <AppTitle>{figma.string("titleText")}</AppTitle>,
})

// ✓ CORRECT - use hardcoded content in conditional JSX
appTitle: figma.boolean("showTitle", {
true: <AppTitle>App Name</AppTitle>,
})

// ✓ CORRECT - or get the string separately and use in example
props: {
titleText: figma.string("titleText"),
},
example: ({titleText}) => <AppTitle>{titleText}</AppTitle>
```

**Trade-off:** You cannot have both conditional rendering AND dynamic text in the same element. Choose one:
- Conditional with hardcoded text (preferred - shows the pattern to users)
- Always shown with dynamic text (when the actual value matters more)

### 8. Extract Shared Props Into Constants

When multiple `figma.connect` calls use the same props, extract them to avoid repetition:

```tsx
const sharedProps = {
size: figma.enum("size", {md: "md"}),
showIcon: figma.boolean("icon", {true: <Icon />}),
}

figma.connect(Component, URL, { variant: {type: "a"}, props: sharedProps, ... })
figma.connect(Component, URL, { variant: {type: "b"}, props: sharedProps, ... })
```

### 9. Showcase Frequently-Used API Features

Code Connect teaches developers how to use components - not just map Figma properties. Hardcode examples of frequently-used props even without Figma property mappings. Review the component's documentation demos to identify high-value props worth including.

### 10. Composite Components

Create separate `figma.connect` calls for sub-components when it makes sense for the examples. Reference the component's documentation page to understand the recommended usage patterns.

### 11. Icon Handling

Prefer `figma.instance("iconProp")` when feasible—getting content from Figma is always better. Hardcode icon names only when instance mapping isn't practical.

### 12. Always Use the User-Provided Node ID

**When the user provides a Figma URL, use that exact node ID.** Do not replace it with variant node IDs found in metadata.

```tsx
// User provides: https://figma.com/design/XXX?node-id=3746-4648

// ❌ WRONG - replacing user's URL with variant node IDs from metadata
figma.connect(Component, "<FIGMA_COMPONENTS_BASE>?node-id=3746-4649", {...}) // variant node
figma.connect(Component, "<FIGMA_COMPONENTS_BASE>?node-id=3746-4651", {...}) // another variant

// ✓ CORRECT - use the user's node ID for ALL connects, differentiate with `variant`
const FIGMA_URL = "<FIGMA_COMPONENTS_BASE>?node-id=3746-4648"

figma.connect(Component, FIGMA_URL, {
variant: {type: "a"},
...
})
figma.connect(Component, FIGMA_URL, {
variant: {type: "b"},
...
})
```

Using variant node IDs will fail validation with: "node is not a top level component or component set"

**Verification workflow:**
1. User provides a URL → extract the node ID
2. Use `get_metadata` or `get_design_context` to verify it's a component set (not a variant)
3. If valid component set → use it directly
4. If it's a documentation frame → ask user which component set within it to use
5. **Never substitute the user's node ID with variant node IDs from metadata**

## File Location

- React: `packages/frameworks/react/src/[component]/figma/[component].figma.tsx`
- Angular: `packages/frameworks/angular/src/[component]/figma/[component].figma.ts`

## Syntax Reference

| Helper | Use Case |
|--------|----------|
| `figma.boolean("prop")` | Boolean toggles |
| `figma.boolean("prop", { true: value })` | Conditional values |
| `figma.enum("prop", { FigmaVal: "codeVal" })` | Variants/enums (omit defaults) |
| `figma.string("prop")` | String properties |
| `figma.textContent("Layer Name")` | Text from layers (not properties) |
| `figma.instance("prop")` | Nested component instances |
| `figma.nestedProps("Layer", { ... })` | Properties on nested components |
| `figma.children("Slot")` | Slot content |

Prefer real imports at file top over `imports` prop (which can cause duplicates when components nest).

## Workflow

1. **Read the component** - understand props, identify defaults
- Check type definitions in `packages/common/qds-core/src/[component]/[component].types.ts`
- **Read component documentation** in `packages/docs/react-docs/src/routes/components+/[component]+/_[component].mdx` for implementation guidelines that should be reflected in examples (required attributes, recommended patterns, accessibility notes, etc.)
- Look for `Qds[Component]Size` or similar type unions to find valid values
- The first value in a union is typically the default (verify in component source)
2. **Verify Figma properties** via MCP tools - don't assume from similar components
3. **Map properties** - plan hardcodes for missing properties
4. **Generate file** - omit defaults, no ternary operators
5. **Run dry-run validation** from `packages/frameworks/react`:
```bash
pnpm figma connect publish --dry-run --config ./figma/components.config.json
```
6. **Verify checklist**:
- [ ] Using user-provided node ID (not variant node IDs from metadata)
- [ ] Every Figma property verified via MCP
- [ ] No default values in enum mappings
- [ ] No ternary/logical operators in example
- [ ] No figma helpers interpolated inside JSX children
- [ ] Missing Figma properties hardcoded appropriately
- [ ] Documentation guidelines reflected in examples
- [ ] Real imports used instead of imports prop where possible
- [ ] Shared props extracted into constants (no duplication across connects)
- [ ] Uncontrolled props used for form/state values
7. **If dry-run fails** - read the error, attempt to fix it. If stuck, report the error to the user.

## Resources

- Figma file: `https://www.figma.com/design/ETvFgN3bbNvr6sbpoZyNuA/branch/G6YKSbQ5Jn83xQBRvlqe6M/Base-Component-Library-v1.0.4`
- Code Connect docs: `https://developers.figma.com/docs/code-connect/react/`
- Code Connect source (when docs are unclear): `https://github.com/figma/code-connect`
35 changes: 35 additions & 0 deletions .github/workflows/publish-figma.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: "Publish Figma"
env:
TURBO_API: "http://127.0.0.1:8585"
TURBO_TEAM: "qualcomm-ui"
TURBO_TOKEN: "turbo-cache-token"

on:
push:
branches: [ main ]
paths:
- 'packages/frameworks/react/**/*.figma.tsx'
- 'packages/frameworks/angular/**/*.figma.ts'
workflow_dispatch:

jobs:
publish-figma:
runs-on: ubuntu-latest
concurrency:
group: PR Publish Figma - ${{github.head_ref}}
cancel-in-progress: true
timeout-minutes: 10
steps:
- uses: actions/checkout@v4

- uses: ./.github/actions/init-node-and-install

- uses: ./.github/actions/init-remote-cache
with:
turborepo-remote-cache-bucket-name: ${{ secrets.TURBOREPO_REMOTE_CACHE_BUCKET_NAME }}
turborepo-remote-cache-access-key: ${{ secrets.TURBOREPO_REMOTE_CACHE_ACCESS_KEY }}
turborepo-remote-cache-secret-key: ${{ secrets.TURBOREPO_REMOTE_CACHE_SECRET_KEY }}

- run: pnpm build --filter @qui/react

- run: pnpm react figma:publish-components
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default defineConfig(
"**/public/exports/**",
"**/frameworks/react-internal/files/component-list.md",
"packages/docs/*/knowledge/**",
"**/*.figma.tsx",
"**/generated/**",
],
},
{
Expand Down
6 changes: 6 additions & 0 deletions packages/common/qds-core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @qualcomm-ui/qds-core Changelog

## 1.18.0 (2026/02/13)

### Features

- feat(link): add `brand` and `white-persistent` emphasis

## 1.17.0 (2026/02/11)

### Features
Expand Down
2 changes: 1 addition & 1 deletion packages/common/qds-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@qualcomm-ui/qds-core",
"version": "1.17.0",
"version": "1.18.0",
"description": "qds core components",
"author": "Ryan Bower",
"license": "BSD-3-Clause-Clear",
Expand Down
9 changes: 9 additions & 0 deletions packages/common/qds-core/src/button/qds-button.css
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,15 @@
--icon-size: 16px;
}
}

/* For when the icon is declared as a node, i.e. `<ExternalLink />` */
.lucide {
height: var(--icon-size);
stroke: currentColor;
stroke-linecap: round;
stroke-linejoin: round;
width: var(--icon-size);
}
}

.qui-button-group {
Expand Down
8 changes: 8 additions & 0 deletions packages/common/qds-core/src/icon/qds-icon.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@
vertical-align: middle;
width: var(--icon-width, var(--icon-size));

svg.lucide {
height: var(--icon-height, var(--icon-size));
stroke: currentColor;
stroke-linecap: round;
stroke-linejoin: round;
width: var(--icon-width, var(--icon-size));
}

&[hidden] {
display: none;
}
Expand Down
5 changes: 5 additions & 0 deletions packages/common/qds-core/src/input/qds-input.css
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@
--icon-width: var(--sizing-80);
margin-right: calc(var(--spacing-80) - var(--input-group-gap));
}

svg.lucide {
height: var(--icon-height);
width: var(--icon-width);
}
}

.qui-input__clear-button {
Expand Down
6 changes: 5 additions & 1 deletion packages/common/qds-core/src/link/link.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import type {DirectionProperty} from "@qualcomm-ui/utils/direction"

import type {linkClasses} from "./link.classes"

export type QdsLinkEmphasis = "default" | "neutral"
export type QdsLinkEmphasis =
| "default"
| "neutral"
| "brand"
| "white-persistent"

export type QdsLinkSize = "xs" | "sm" | "md" | "lg" | "xl" | "xxl"

Expand Down
Loading
Loading