diff --git a/.cursor/rules/figma-rules.mdc b/.cursor/rules/figma-rules.mdc
index 62b008a2771..b8c1ec878e2 100644
--- a/.cursor/rules/figma-rules.mdc
+++ b/.cursor/rules/figma-rules.mdc
@@ -3,23 +3,22 @@ description:
globs:
alwaysApply: true
---
-
+
---
description: Figma Dev Mode MCP rules
-globs:
+globs:
alwaysApply: true
---
- The Figma Dev Mode MCP Server provides an assets endpoint which can serve image and SVG assets
- - IMPORTANT: If the Figma Dev Mode MCP Server returns a localhost source for an image or an SVG, use that image or SVG source directly
- - IMPORTANT: DO NOT import/add new icon packages, all the assets should be in the Figma payload
- IMPORTANT: do NOT use or create placeholders if a localhost source is provided
- IMPORTANT: Always use components from `/packages` whenever possible
- - Prioritize using semantic tokens and component props over Figma fidelity
- - Avoid hardcoded values, use semantic design tokens from Figma whenever possible
- - Follow WCAG requirements for accessibility
- - Add component documentation
- - Place UI components in `/package/gamut`; avoid inline styles unless truly necessary
- - Add Best Practices blah blah blah
-
-
-
+ - Prioritize using semantic tokens and component props over Figma fidelity
+ - Avoid hardcoded values, use semantic design tokens whenever possible. The Background component is the exception - use color token names (i.e, navy, white, etc) and not a semantic token
+ - IMPORTANT: Do not use inline styles, whenever possible use emotion and the css-in-js utilities from `/packages/gamut-styles`. Follow the rules from `packages/styleguide/src/lib/Foundations/System`
+ - IMPORTANT: Follow WCAG requirements for accessibility
+ - Always follow best practices from `/packages/styleguide/src/lib/Meta/Best Practices.mdx`
+ - IMPORTANT: All patterns should come from `/packages/gamut-patterns` - match the Figma component name to the pattern component
+ - IMPORTANT: All icons should come from `/packages/gamut-icons` - match the Figma component name to the icon component
+ - Check if the Figma component name matches a Gamut component name and use that component
+ - Use the tokens from `packages/gamut-styles/src/variables`
+ - Use the CodeConnect implementation
\ No newline at end of file
diff --git a/figma.config.json b/figma.config.json
index f8d35f39eda..6da98ba9501 100644
--- a/figma.config.json
+++ b/figma.config.json
@@ -1,6 +1,6 @@
{
"codeConnect": {
- "include": ["packages/gamut/src/**/*.{tsx,jsx}"],
+ "include": ["packages/gamut/src/**/*.{tsx,jsx}","packages/gamut-icons/figma/*.{tsx,jsx}"],
"label": "React",
"interactiveSetupFigmaFileUrl": "https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=23-5&p=f&m=dev"
}
diff --git a/packages/gamut/src/Badge/index.figma.tsx b/packages/gamut/src/Badge/index.figma.tsx
index a0dabd0d32e..e0ba72c8a69 100644
--- a/packages/gamut/src/Badge/index.figma.tsx
+++ b/packages/gamut/src/Badge/index.figma.tsx
@@ -1,6 +1,7 @@
-import React from "react"
-import { Badge } from "./index"
-import figma from "@figma/code-connect"
+import figma from '@figma/code-connect';
+import React from 'react';
+
+import { Badge } from './index';
/**
* -- This file was auto-generated by Code Connect --
@@ -12,24 +13,23 @@ import figma from "@figma/code-connect"
figma.connect(
Badge,
- "https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=8200%3A8349",
+ 'https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=8200%3A8349',
{
props: {
- label: figma.string("✏️ label"),
- leadingicon: figma.boolean("leading-icon"),
- icon: figma.instance("↳ icon"),
- variant: figma.enum("variant", {
- primary: "primary",
- secondary: "secondary",
- tertiary: "tertiary",
- "tertiary-fill": "tertiary-fill",
- accent: "accent",
+ label: figma.string('✏️ label'),
+ icon: figma.instance('↳ icon'),
+ variant: figma.enum('variant', {
+ primary: 'primary',
+ secondary: 'secondary',
+ tertiary: 'tertiary',
+ 'tertiary-fill': 'tertiary-fill',
+ accent: 'accent',
}),
- size: figma.enum("size", {
- base: "base",
- sm: "sm",
+ size: figma.enum('size', {
+ base: 'base',
+ sm: 'sm',
}),
},
- example: (props) => ,
- },
-)
+ example: (props) => ,
+ }
+);
diff --git a/packages/gamut/src/Disclosure/index.figma.tsx b/packages/gamut/src/Disclosure/index.figma.tsx
index b8ddcd31860..ad9651dc125 100644
--- a/packages/gamut/src/Disclosure/index.figma.tsx
+++ b/packages/gamut/src/Disclosure/index.figma.tsx
@@ -1,6 +1,7 @@
-import React from "react"
-import { Disclosure } from "./index"
-import figma from "@figma/code-connect"
+import figma from '@figma/code-connect';
+import React from 'react';
+
+import { Disclosure } from './index';
/**
* -- This file was auto-generated by Code Connect --
@@ -12,22 +13,22 @@ import figma from "@figma/code-connect"
figma.connect(
Disclosure,
- "https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=43343%3A19299",
+ 'https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=43343%3A19299',
{
props: {
- variant: figma.enum("variant", {
- default: "default",
- subtle: "subtle",
- transparent: "transparent",
+ variant: figma.enum('variant', {
+ default: 'default',
+ subtle: 'subtle',
+ transparent: 'transparent',
}),
- hasBorder: figma.boolean("hasBorder"),
- spacing: figma.enum("spacing", {
- normal: "normal",
- condensed: "condensed",
- compact: "compact",
+ hasBorder: figma.boolean('hasBorder'),
+ spacing: figma.enum('spacing', {
+ normal: 'normal',
+ condensed: 'condensed',
+ compact: 'compact',
}),
- isExpanded: figma.boolean("isExpanded"),
+ isExpanded: figma.boolean('isExpanded'),
},
- example: (props) => ,
- },
-)
+ example: (props) => ,
+ }
+);
diff --git a/packages/gamut/src/Menu/Menu.figma.tsx b/packages/gamut/src/Menu/Menu.figma.tsx
index 37b30fc8614..64e867d84a5 100644
--- a/packages/gamut/src/Menu/Menu.figma.tsx
+++ b/packages/gamut/src/Menu/Menu.figma.tsx
@@ -1,6 +1,7 @@
-import React from "react"
-import { Menu } from "./Menu"
-import figma from "@figma/code-connect"
+import figma from '@figma/code-connect';
+import React from 'react';
+
+import { Menu } from './Menu';
/**
* -- This file was auto-generated by Code Connect --
@@ -12,23 +13,20 @@ import figma from "@figma/code-connect"
figma.connect(
Menu,
- "https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=1971%3A2562",
+ 'https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=1971%3A2562',
{
props: {
- option3: figma.boolean("option-3"),
- option4: figma.boolean("option-4"),
- option5: figma.boolean("option-5"),
- option6: figma.boolean("option-6"),
- menuSeparator: figma.boolean("menuSeparator"),
- variant: figma.enum("Variant", {
- popover: "popover",
- fixed: "fixed",
+ menuSeparator: figma.boolean('menuSeparator'),
+ variant: figma.enum('Variant', {
+ popover: 'popover',
+ fixed: 'fixed',
}),
- spacing: figma.enum("Spacing", {
- normal: "normal",
- condensed: "condensed",
+ spacing: figma.enum('Spacing', {
+ normal: 'normal',
+ condensed: 'condensed',
}),
+ children: figma.children('*'),
},
- example: (props) =>
,
- },
-)
+ example: (props) => ,
+ }
+);
diff --git a/packages/gamut/src/Menu/MenuItem.figma.tsx b/packages/gamut/src/Menu/MenuItem.figma.tsx
deleted file mode 100644
index 0c565e318e3..00000000000
--- a/packages/gamut/src/Menu/MenuItem.figma.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from "react"
-import { MenuItem } from "./MenuItem"
-import figma from "@figma/code-connect"
-
-/**
- * -- This file was auto-generated by Code Connect --
- * `props` includes a mapping from Figma properties and variants to
- * suggested values. You should update this to match the props of your
- * code component, and update the `example` function to return the
- * code example you'd like to see in Figma
- */
-
-figma.connect(
- MenuItem,
- "https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=57295%3A20592",
- {
- props: {
- leadingIcon: figma.boolean("leadingIcon"),
- trailingIcon: figma.boolean("trailingIcon"),
- label: figma.string("label"),
- state: figma.enum("state", {
- default: "default",
- hover: "hover",
- selected: "selected",
- }),
- },
- example: (props) => ,
- },
-)
diff --git a/packages/gamut/src/Menu/MenuItemFixed.figma.tsx b/packages/gamut/src/Menu/MenuItemFixed.figma.tsx
new file mode 100644
index 00000000000..337937aad5d
--- /dev/null
+++ b/packages/gamut/src/Menu/MenuItemFixed.figma.tsx
@@ -0,0 +1,33 @@
+import figma from '@figma/code-connect';
+import React from 'react';
+
+import { MenuItem } from './MenuItem';
+
+/**
+ * -- This file was auto-generated by Code Connect --
+ * `props` includes a mapping from Figma properties and variants to
+ * suggested values. You should update this to match the props of your
+ * code component, and update the `example` function to return the
+ * code example you'd like to see in Figma
+ */
+
+figma.connect(
+ MenuItem,
+ 'https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=1971-2476',
+ {
+ props: {
+ active: figma.boolean('active'),
+ hover: figma.boolean('hover'),
+ label: figma.string('label'),
+ leadingIcon: figma.boolean('leading-icon'),
+ icon: figma.children('icon*'),
+ },
+ example: ({ label, leadingIcon, icon, ...props }) => {
+ return (
+
+ );
+ },
+ }
+);
diff --git a/packages/gamut/src/Menu/MenuItemPopover.figma.tsx b/packages/gamut/src/Menu/MenuItemPopover.figma.tsx
new file mode 100644
index 00000000000..c84562afb0a
--- /dev/null
+++ b/packages/gamut/src/Menu/MenuItemPopover.figma.tsx
@@ -0,0 +1,33 @@
+import figma from '@figma/code-connect';
+import React from 'react';
+
+import { MenuItem } from './MenuItem';
+
+/**
+ * -- This file was auto-generated by Code Connect --
+ * `props` includes a mapping from Figma properties and variants to
+ * suggested values. You should update this to match the props of your
+ * code component, and update the `example` function to return the
+ * code example you'd like to see in Figma
+ */
+
+figma.connect(
+ MenuItem,
+ 'https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=1971-2489',
+ {
+ props: {
+ active: figma.boolean('active'),
+ hover: figma.boolean('hover'),
+ label: figma.string('label'),
+ leadingIcon: figma.boolean('leading-icon'),
+ icon: figma.children('icon*'),
+ },
+ example: ({ label, leadingIcon, icon, ...props }) => {
+ return (
+
+ );
+ },
+ }
+);
diff --git a/script/jest/icon-figma.js b/script/jest/icon-figma.js
new file mode 100644
index 00000000000..d0530cd12ef
--- /dev/null
+++ b/script/jest/icon-figma.js
@@ -0,0 +1,52 @@
+import { client } from '@figma/code-connect';
+import fs from 'fs';
+
+async function generateIcons() {
+ // fetch components from a figma file. If the `node-id` query parameter is used,
+ // only components within those frames will be included. This is useful if your
+ // file is very large, as this will speed up the query by a lot
+ let components = await client.getComponents(
+ 'https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=38228-2739'
+ );
+
+ // Converts icon names from e.g `icon-32-list` to `Icon32List`
+ components = components
+ .filter(({ name }) => {
+ return name.includes('icon');
+ })
+ .map((component) => ({
+ ...component,
+ name: component.name
+ .split(/[.-]/g)
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
+ .join(''),
+ }));
+
+ const uniqueNames = new Set([...components.map((c) => c.name)]);
+
+ fs.writeFileSync(
+ 'icons.figma.tsx',
+ `\
+ import figma from '@figma/code-connect'
+
+ import {
+ ${Array.from(uniqueNames)
+ .map((iconName) => ` ${iconName},`)
+ .join('\n')}
+ } from './packages/gamut-icons/icons/regular'
+
+ const props = {
+ height: figma.string('Height'),
+ width: figma.string('Width'),
+ color
+
+}
+
+ ${components
+ .map((c) => `figma.connect(${c.name}, '${c.figmaUrl}')`)
+ .join('\n')}
+ `
+ );
+}
+
+generateIcons();