# Contributing to Silver Icons ## Project Overview This is a **React icon library** for SilverAssist, built as an NPM package. Icons are sourced from Figma and converted to React components with a standardized API. **Key Architecture:** - Source: Figma design file (SilverAssist-Library) - Build: TypeScript + tsup → dual ESM/CJS outputs - Distribution: NPM package `@silverassist/icons` ## Project Structure ``` silver-icons/ ├── src/ │ ├── icons/ # 168 icon components │ │ ├── Quality.tsx # Large icons (viewBox="0 0 100 100") │ │ ├── Healthcare.tsx │ │ ├── Calculator.tsx # Small icons (viewBox="0 0 50 50") │ │ └── index.ts # Auto-generated exports │ └── index.ts # Main entry point ├── dist/ # Build output (gitignored) │ ├── index.js # CommonJS │ ├── index.mjs # ES Modules │ └── index.d.ts # TypeScript definitions ├── scripts/ │ ├── utils.js # Shared utilities (naming, attribute conversion) │ ├── generate-icons-list.js # Auto-generates icons-list.txt │ ├── bulk-convert.js # Converts SVG → React components │ └── convert-svg.js # Single SVG converter ├── temp-svgs/ # Figma SVG exports (gitignored) │ └── icons-list.txt # Auto-generated mapping file ├── .github/ │ └── copilot-instructions.md # AI agent guidance ├── package.json ├── tsconfig.json └── tsup.config.ts ``` ## Icon Component Pattern **ALL icons MUST follow this exact structure:** ```tsx import React from "react"; export default function IconNameSVG(props: React.ComponentProps<"svg">) { const { width = 100, height = 100, fill = "#E3F7FB", stroke = "#3F3F3F" } = props; return ( {/* SVG paths here */} ); } ``` **Critical conventions:** - Component name: `{IconName}SVG` (PascalCase + SVG suffix) - Default props: `width=100, height=100, fill="#E3F7FB", stroke="#3F3F3F"` - ViewBox: `0 0 100 100` for large icons, `0 0 50 50` for small icons - Color replacement: Replace `fill="#E3F7FB"` → `fill={fill}` in paths - Props type: `React.ComponentProps<"svg">` (NOT custom IconProps) ## Design System Constants - **Default fill**: `#E3F7FB` (light cyan - SilverAssist brand) - **Default stroke**: `#3F3F3F` (dark gray) - **Large icons**: 100×100px (most icons) - **Small icons**: 50×50px (solid variants) ## Adding New Icons from Figma ### Step 1: Export SVG from Figma 1. Open the Figma file: [SilverAssist-Library](https://www.figma.com/design/9b9lqV6JVDsaOFFn7FjJrL/SilverAssist-Library) 2. Navigate to the icon you want to export 3. Select the icon frame 4. Export as SVG with these settings: - Format: SVG - Include "id" attribute: ✓ 5. Save to `temp-svgs/` folder **Naming conventions from Figma:** - Large icons: `Generic Icons Alt={name}.svg` → `{Name}SVG` - Small icons: `Generic Solid Icons={name}.svg` → `{Name}SVG` - Examples: - `Generic Icons Alt=Quality.svg` → `QualitySVG` - `Generic Solid Icons=Bridge Loan.svg` → `BridgeLoanSVG` ### Step 2: Convert Single Icon ```bash npm run convert-svg ``` Example: ```bash npm run convert-svg "Generic Icons Alt=Quality.svg" Quality ``` This will: - Convert SVG to React component - Apply color parameterization - Fix style attributes - Create `src/icons/Quality.tsx` ### Step 3: Bulk Convert Multiple Icons If you have many icons to convert: 1. Export all SVGs to `temp-svgs/` folder 2. Auto-generate the mapping file: ```bash npm run generate-list ``` This creates `temp-svgs/icons-list.txt` with format: ``` filename.svg|ComponentName|Description ``` 3. Convert all icons at once: ```bash npm run bulk-convert ``` This will: - Process all icons in `icons-list.txt` - Create React components in `src/icons/` - Auto-update `src/icons/index.ts` with exports ### Step 4: Validate and Build ```bash # Check TypeScript npm run typecheck # Build the library npm run build ``` ## Naming Rules **From Figma to Component:** - `Generic Icons Alt=Quality` → `QualitySVG` - `Generic Solid Icons=Bridge Loan` → `BridgeLoanSVG` - `Generic Icons Alt=360 Tour` → `Icon360TourSVG` (numbers prefixed) - `Generic Icons Alt=Q&A` → `QAndASVG` (`&` becomes `And`) - `Generic Icons Alt=What?` → `WhatSVG` (`?` removed) **Special character handling:** - Numbers at start → prefix with "Icon" - `&` → `And` - `?` → removed - Spaces → removed (PascalCase) ## Build System **Commands:** - `npm run build` - Build dist/ with tsup (CJS + ESM + types) - `npm run dev` - Watch mode for development - `npm run typecheck` - Validate TypeScript without building **Important:** Always run `typecheck` before building. The build system (tsup) bundles to `dist/` with dual format exports. ## Conversion Scripts All conversion scripts share common utilities from `scripts/utils.js` to ensure consistency and maintainability. ### `scripts/utils.js` Shared utilities used by all conversion scripts: **Functions:** - `convertAttributesToCamelCase(svg)` - Converts kebab-case SVG attributes to camelCase for React - `convertToComponentName(name)` - Converts Figma names to PascalCase component names - `generateDescription(name)` - Generates human-readable descriptions from icon names - `generateDescriptionFromComponentName(componentName)` - Generates descriptions from component names **Supported attribute conversions:** - `clip-path` → `clipPath`, `clip-rule` → `clipRule` - `fill-opacity` → `fillOpacity`, `fill-rule` → `fillRule` - `stroke-*` attributes (width, dasharray, linecap, etc.) - Font and text attributes (font-family, text-anchor, etc.) - Gradient attributes (stop-color, stop-opacity) ### `scripts/generate-icons-list.js` Auto-generates `temp-svgs/icons-list.txt` from SVG files in `temp-svgs/` folder. **Logic:** - Detects naming pattern (Alt= or Solid Icons=) - Uses `convertToComponentName()` for PascalCase conversion - Handles special characters and number prefixing - Outputs: `filename.svg|ComponentName|Auto-generated from {filename}` ### `scripts/bulk-convert.js` Converts all SVGs listed in `icons-list.txt` to React components. **Transformations:** - Replaces `fill="#E3F7FB"` → `fill={fill}` - Replaces `stroke="#3F3F3F"` → `stroke={stroke}` - Uses `convertAttributesToCamelCase()` for all SVG attributes - Converts style attributes: `style="mix-blend-mode:multiply"` → `style={{ mixBlendMode: "multiply" }}` - Detects viewBox size (50x50 vs 100x100) for default width/height - Auto-updates `src/icons/index.ts` with alphabetically sorted exports ### `scripts/convert-svg.js` Converts a single SVG file. **Usage:** ```bash npm run convert-svg ``` **Transformations:** Same as bulk-convert.js, uses shared utilities from utils.js ## Testing Icons Locally 1. **Link the package:** ```bash npm link ``` 2. **In your Next.js project:** ```bash npm link @silverassist/icons ``` 3. **Use the icons:** ```tsx import { QualitySVG } from "@silverassist/icons"; export default function Page() { return ; } ``` ## Publishing to NPM 1. **Update version in package.json:** ```json { "version": "0.2.0" } ``` 2. **Login to NPM (if not already):** ```bash npm login ``` Use your credentials for the `silverassist` organization. 3. **Publish:** ```bash npm publish --access public ``` The `prepublishOnly` script will automatically run the build. ## Version Management Follow semantic versioning: - **Patch** (0.1.x): Bug fixes, small tweaks - **Minor** (0.x.0): New icons added - **Major** (x.0.0): Breaking changes to component API ## Icon Statistics - **Total icons**: 168 React components - **Large icons**: 115 (viewBox "0 0 100 100") - **Small icons**: 53 (viewBox "0 0 50 50") - **Build size**: ~1.33 MB (ESM), ~1.35 MB (CJS) - **TypeScript definitions**: ~27 KB ## Common Issues ### TypeScript errors after adding icons Run `npm run typecheck` to see errors. Common causes: - Component name starts with number (prefix with "Icon") - Style attribute as string instead of object - Missing export in `src/icons/index.ts` ### Icons not showing correct colors Check that: - `fill={fill}` is used in paths, not hardcoded `#E3F7FB` - `stroke={stroke}` is used in paths, not hardcoded `#3F3F3F` - Props are properly destructured in component ### Build warnings If you see warnings about package.json exports, ensure `types` comes before `import` and `require`: ```json "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.mjs", "require": "./dist/index.js" } } ``` ## Coding Standards ### Documentation **All documentation MUST be in English.** #### JavaScript Files Use JSDoc standard with type definitions for all exported functions: ```javascript /** * Convert kebab-case SVG attributes to camelCase for React * @param {string} svg - SVG content string * @returns {string} SVG with camelCase attributes */ function convertAttributesToCamelCase(svg) { // Map of SVG attributes that need to be converted const attributeMap = {/* ... */}; // implementation } /** * Convert Figma name to PascalCase component name * @param {string} name - Original name from Figma * @returns {string} PascalCase component name */ function convertToComponentName(name) { // implementation } ``` **Required JSDoc tags:** - `@param {type} name - description` for all parameters - `@returns {type} description` for return values - Description of what the function does #### TypeScript Files Use TSDoc comments for React components: ```typescript /** * Quality icon component * Represents quality and excellence in SilverAssist services */ export default function QualitySVG(props: React.ComponentProps<"svg">) { const { width = 100, height = 100, fill = "#E3F7FB", stroke = "#3F3F3F" } = props; return ( {/* SVG content */} ); } ``` ### Naming Conventions #### Files - **React components:** `PascalCase.tsx` - Examples: `Quality.tsx`, `Healthcare.tsx`, `BridgeLoan.tsx` - **Utility scripts:** `kebab-case.js` - Examples: `convert-svg.js`, `bulk-convert.js`, `generate-icons-list.js` - **Shared utilities:** `camelCase.js` - Examples: `utils.js` - **Config files:** `kebab-case` or `camelCase` - Examples: `tsup.config.ts`, `package.json` #### Variables and Functions - **Variables:** `camelCase` ```javascript const svgContent = fs.readFileSync(svgPath, "utf-8"); const iconName = "Quality"; const defaultSize = 100; ``` - **Functions:** `camelCase` ```javascript function convertToComponentName(name) { /* ... */ } function generateDescription(iconName) { /* ... */ } function updateIndexFile(newIcons) { /* ... */ } ``` - **Constants:** `UPPER_SNAKE_CASE` or `camelCase` for paths ```javascript const DEFAULT_FILL = "#E3F7FB"; const DEFAULT_STROKE = "#3F3F3F"; const tempSvgsDir = path.join(__dirname, "../temp-svgs"); ``` - **React Components:** `PascalCase` with `SVG` suffix ```typescript export default function QualitySVG(props) { /* ... */ } export default function HealthcareSVG(props) { /* ... */ } ``` #### Function Parameters - Use descriptive `camelCase` names - Always document types in JSDoc for JavaScript ```javascript /** * @param {string} svgPath - Absolute path to SVG file * @param {string} componentName - PascalCase component name * @param {string} description - Human-readable description */ function convertSvgToComponent(svgPath, componentName, description) { // implementation } ``` ### Code Style #### JavaScript/Node.js Scripts ```javascript // Good ✓ const svgContent = fs.readFileSync(svgPath, "utf-8"); const componentName = convertToComponentName(name); const description = `${name} icon`; if (isValid) { processIcon(svgContent); } // Bad ✗ var svg_content = fs.readFileSync(svg_path, "utf-8"); // var, snake_case, double quotes const ComponentName = convert_to_component_name(name); // PascalCase for variable const description = name + " icon"; // string concatenation instead of template literal if (isValid) { // missing spaces processIcon(svgContent); } // missing semicolon ``` **Rules:** - Use `const` and `let`, never `var` - Single quotes for strings: `'hello'` not `"hello"` - Template literals for interpolation: `` `${name} icon` `` - Semicolons required at end of statements - 2-space indentation (no tabs) - Use `require()` for imports (CommonJS) - Spaces around operators and keywords - No trailing whitespace #### TypeScript Components ```typescript // Good ✓ import React from 'react'; export default function QualitySVG(props: React.ComponentProps<"svg">) { const { width = 100, height = 100, fill = "#E3F7FB" } = props; return ( ); } // Bad ✗ import React from "react"; // double quotes for imports export default function QualitySVG(props: any) { // any type const { width = 100, height = 100, fill = '#E3F7FB' } = props; // single quotes in JSX context return // single quotes for JSX // inconsistent quotes } ``` **Rules:** - Use `import` statements (ES Modules) - Proper type annotations for all props - No `any` types - use specific types or `React.ComponentProps<>` - Double quotes for JSX attributes - Single quotes for imports - Template literals for dynamic content - Destructure props for clarity #### Comments ```javascript // Good ✓ /** * Convert Figma naming to React component name * Handles special characters and number prefixes */ function convertToComponentName(name) { // Replace & with And for valid JavaScript identifiers let componentName = name.replace(/\s*&\s*/g, " And "); // Prefix with "Icon" if name starts with number if (/^\d/.test(componentName)) { componentName = "Icon" + componentName; } return componentName; } // Bad ✗ // converts name function convertToComponentName(name) { // missing JSDoc let componentName = name.replace(/\s*&\s*/g, " And "); // no explanation why if (/^\d/.test(componentName)) { componentName = "Icon" + componentName; // what does this do? } return componentName; } ``` **Rules:** - Use JSDoc for all exported functions - Inline comments for complex logic - Explain _why_, not _what_ (code should be self-explanatory) - Keep comments concise and in English - Update comments when code changes ## License Polyform Noncommercial License 1.0.0 - Silver Assist Team This is NOT MIT. The library is for internal use and SilverAssist projects only.