{"id":"b969af37-43e1-4362-bbe1-c3773161c997","shortId":"2Jr8Ss","kind":"skill","title":"salesforce-component-standards","tagline":"Quality standards for Salesforce Lightning Web Components (LWC), Aura components, and Visualforce pages. Covers SLDS 2 compliance, accessibility (WCAG 2.1 AA), data access pattern selection, component communication rules, XSS prevention, CSRF enforcement, FLS/CRUD in AuraEnabled ","description":"# Salesforce Component Quality Standards\n\nApply these checks to every LWC, Aura component, and Visualforce page you write or review.\n\n## Section 1 — LWC Quality Standards\n\n### 1.1 Data Access Pattern Selection\n\nChoose the right data access pattern before writing JavaScript controller code:\n\n| Use case | Pattern | Why |\n|---|---|---|\n| Read a single record reactively (follows navigation) | `@wire(getRecord, { recordId, fields })` | Lightning Data Service — cached, reactive |\n| Standard CRUD form for a single object | `<lightning-record-form>` or `<lightning-record-edit-form>` | Built-in FLS, CRUD, and accessibility |\n| Complex server query or filtered list | `@wire(apexMethodName, { param })` on a `cacheable=true` method | Allows caching; wire re-fires on param change |\n| User-triggered action, DML, or non-cacheable server call | Imperative `apexMethodName(params).then(...).catch(...)` | Required for DML — wired methods cannot be `@AuraEnabled` without `cacheable=true` |\n| Cross-component communication (no shared parent) | Lightning Message Service (LMS) | Decoupled, works across DOM boundaries |\n| Multi-object graph relationships | GraphQL `@wire(gql, { query, variables })` | Single round-trip for complex related data |\n\n### 1.2 Security Rules\n\n| Rule | Enforcement |\n|---|---|\n| No raw user data in `innerHTML` | Use `{expression}` binding in the template — the framework auto-escapes. Never use `this.template.querySelector('.el').innerHTML = userValue` |\n| Apex `@AuraEnabled` methods enforce CRUD/FLS | Use `WITH USER_MODE` in SOQL or explicit `Schema.sObjectType` checks |\n| No hardcoded org-specific IDs in component JavaScript | Query or pass as a prop — never embed record IDs in source |\n| `@api` properties from parent: validate before use | A parent can pass anything — validate type and range before using as a query parameter |\n\n### 1.3 SLDS 2 and Styling Standards\n\n- **Never** hardcode colours: `color: #FF3366` → use `color: var(--slds-c-button-brand-color-background)` or a semantic SLDS token.\n- **Never** override SLDS classes with `!important` — compose with custom CSS properties.\n- Use `<lightning-*>` base components wherever they exist: `lightning-button`, `lightning-input`, `lightning-datatable`, `lightning-card`, etc.\n- Base components include built-in SLDS 2, dark mode, and accessibility — avoid reimplementing their behaviour.\n- If using custom CSS, test in both **light mode** and **dark mode** before declaring done.\n\n### 1.4 Accessibility Requirements (WCAG 2.1 AA)\n\nEvery LWC component must pass all of these before it is considered done:\n\n- [ ] All form inputs have `<label>` or `aria-label` — never use placeholder as the only label\n- [ ] All icon-only buttons have `alternative-text` or `aria-label` describing the action\n- [ ] All interactive elements are reachable and operable by keyboard (Tab, Enter, Space, Escape)\n- [ ] Colour is not the only means of conveying status — pair with text, icon, or `aria-*` attributes\n- [ ] Error messages are associated with their input via `aria-describedby`\n- [ ] Focus management is correct in modals — focus moves into the modal on open and back on close\n\n### 1.5 Component Communication Rules\n\n| Direction | Mechanism |\n|---|---|\n| Parent → Child | `@api` property or calling a `@api` method |\n| Child → Parent | `CustomEvent` — `this.dispatchEvent(new CustomEvent('eventname', { detail: data }))` |\n| Sibling / unrelated components | Lightning Message Service (LMS) |\n| Never use | `document.querySelector`, `window.*`, or Pub/Sub libraries |\n\nFor Flow screen components:\n- Events that need to reach the Flow runtime must set `bubbles: true` and `composed: true`.\n- Expose `@api value` for two-way binding with the Flow variable.\n\n### 1.6 JavaScript Performance Rules\n\n- **No side effects in `connectedCallback`**: it runs on every DOM attach — avoid DML, heavy computation, or rendering state mutations here.\n- **Guard `renderedCallback`**: always use a boolean guard to prevent infinite render loops.\n- **Avoid reactive property traps**: setting a reactive property inside `renderedCallback` causes a re-render — use it only when necessary and guarded.\n- **Do not store large datasets in component state** — paginate or stream large results instead.\n\n### 1.7 Jest Test Requirements\n\nEvery component that handles user interaction or retrieves Apex data must have a Jest test:\n\n```javascript\n// Minimum test coverage expectations\nit('renders the component with correct title', async () => { ... });\nit('calls apex method and displays results', async () => { ... });  // Wire mock\nit('dispatches event when button is clicked', async () => { ... });\nit('shows error state when apex call fails', async () => { ... }); // Error path\n```\n\nUse `@salesforce/sfdx-lwc-jest` mocking utilities:\n- `wire` adapter mocking: `setImmediate` + `emit({ data, error })`\n- Apex method mocking: `jest.mock('@salesforce/apex/MyClass.myMethod', ...)`\n\n---\n\n## Section 2 — Aura Component Standards\n\n### 2.1 When to Use Aura vs LWC\n\n- **New components: always LWC** unless the target context is Aura-only (e.g. extending `force:appPage`, using Aura-specific events in a legacy managed package).\n- **Migrating Aura to LWC**: prefer LWC, migrate component-by-component; LWC can be embedded inside Aura components.\n\n### 2.2 Aura Security Rules\n\n- `@AuraEnabled` controller methods must declare `with sharing` and enforce CRUD/FLS — Aura does **not** enforce them automatically.\n- Never use `{!v.something}` with unescaped user data in `<div>` unbound helpers — use `<ui:outputText value=\"{!v.text}\" />` or `<c:something>` to escape.\n- Validate all inputs from component attributes before using them in SOQL / Apex logic.\n\n### 2.3 Aura Event Design\n\n- **Component events** for parent-child communication — lowest scope.\n- **Application events** only when component events cannot reach the target — they broadcast to the entire app and can be a performance and maintenance problem.\n- For hybrid LWC + Aura stacks: use Lightning Message Service to decouple communication — do not rely on Aura application events reaching LWC components.\n\n---\n\n## Section 3 — Visualforce Security Standards\n\n### 3.1 XSS Prevention\n\n```xml\n<!-- ❌ NEVER — renders raw user input as HTML -->\n<apex:outputText value=\"{!userInput}\" escape=\"false\" />\n\n<!-- ✅ ALWAYS — auto-escaping on -->\n<apex:outputText value=\"{!userInput}\" />\n<!-- Default escape=\"true\" — platform HTML-encodes the output -->\n```\n\nRule: `escape=\"false\"` is never acceptable for user-controlled data. If rich text must be rendered, sanitise server-side with a whitelist before output.\n\n### 3.2 CSRF Protection\n\nUse `<apex:form>` for all postback actions — the platform injects a CSRF token automatically into the form. Do **not** use raw `<form method=\"POST\">` HTML elements, which bypass CSRF protection.\n\n### 3.3 SOQL Injection Prevention in Controllers\n\n```apex\n// ❌ NEVER\nString soql = 'SELECT Id FROM Account WHERE Name = \\'' + ApexPages.currentPage().getParameters().get('name') + '\\'';\nList<Account> results = Database.query(soql);\n\n// ✅ ALWAYS — bind variable\nString nameParam = ApexPages.currentPage().getParameters().get('name');\nList<Account> results = [SELECT Id FROM Account WHERE Name = :nameParam];\n```\n\n### 3.4 View State Management Checklist\n\n- [ ] View state is under 135 KB (check in browser developer tools or the Salesforce View State tab)\n- [ ] Fields used only for server-side calculations are declared `transient`\n- [ ] Large collections are not persisted across postbacks unnecessarily\n- [ ] `readonly=\"true\"` is set on `<apex:page>` for read-only pages to skip view-state serialisation\n\n### 3.5 FLS / CRUD in Visualforce Controllers\n\n```apex\n// Before reading a field\nif (!Schema.sObjectType.Account.fields.Revenue__c.isAccessible()) {\n    ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'You do not have access to this field.'));\n    return null;\n}\n\n// Before performing DML\nif (!Schema.sObjectType.Account.isDeletable()) {\n    throw new System.NoAccessException();\n}\n```\n\nStandard controllers enforce FLS for bound fields automatically. **Custom controllers do not** — FLS must be enforced manually.\n\n---\n\n## Quick Reference — Component Anti-Patterns Summary\n\n| Anti-pattern | Technology | Risk | Fix |\n|---|---|---|---|\n| `innerHTML` with user data | LWC | XSS | Use template bindings `{expression}` |\n| Hardcoded hex colours | LWC/Aura | Dark-mode / SLDS 2 break | Use SLDS CSS custom properties |\n| Missing `aria-label` on icon buttons | LWC/Aura/VF | Accessibility failure | Add `alternative-text` or `aria-label` |\n| No guard in `renderedCallback` | LWC | Infinite rerender loop | Add `hasRendered` boolean guard |\n| Application event for parent-child | Aura | Unnecessary broadcast scope | Use component event instead |\n| `escape=\"false\"` on user data | Visualforce | XSS | Remove — use default escaping |\n| Raw `<form>` postback | Visualforce | CSRF vulnerability | Use `<apex:form>` |\n| No `with sharing` on custom controller | VF / Apex | Data exposure | Add `with sharing` declaration |\n| FLS not checked in custom controller | VF / Apex | Privilege escalation | Add `Schema.sObjectType` checks |\n| SOQL concatenated with URL param | VF / Apex | SOQL injection | Use bind variables |","tags":["salesforce","component","standards","awesome","copilot","github","agent-skills","agents","custom-agents","github-copilot","hacktoberfest","prompt-engineering"],"capabilities":["skill","source-github","skill-salesforce-component-standards","topic-agent-skills","topic-agents","topic-awesome","topic-custom-agents","topic-github-copilot","topic-hacktoberfest","topic-prompt-engineering"],"categories":["awesome-copilot"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/github/awesome-copilot/salesforce-component-standards","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add github/awesome-copilot","source_repo":"https://github.com/github/awesome-copilot","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 30743 github stars · SKILL.md body (9,259 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-22T00:52:17.194Z","embedding":null,"createdAt":"2026-04-18T21:51:10.624Z","updatedAt":"2026-04-22T00:52:17.194Z","lastSeenAt":"2026-04-22T00:52:17.194Z","tsv":"'1':60 '1.1':64 '1.2':199 '1.3':285 '1.4':373 '1.5':480 '1.6':549 '1.7':621 '135':974 '2':20,287,349,699,1106 '2.1':24,377,703 '2.2':754 '2.3':801 '3':861 '3.1':865 '3.2':895 '3.3':923 '3.4':965 '3.5':1022 'aa':25,378 'accept':874 'access':22,27,66,73,114,353,374,1044,1121 'account':936,961 'across':178,1003 'action':141,422,902 'adapt':687 'add':1123,1139,1184,1198 'allow':129 'altern':414,1125 'alternative-text':413,1124 'alway':575,712,947 'anti':1079,1083 'anti-pattern':1078,1082 'anyth':274 'apex':227,633,655,676,693,799,929,1028,1181,1195,1207 'apexmethodnam':122,150 'apexpages.addmessage':1036 'apexpages.currentpage':939,952 'apexpages.message':1038 'apexpages.severity.error':1039 'api':263,488,493,538 'app':829 'appli':44 'applic':814,855,1143 'apppag':725 'aria':398,418,450,461,1115,1129 'aria-describedbi':460 'aria-label':397,417,1114,1128 'associ':455 'async':652,660,670,679 'attach':563 'attribut':451,793 'aura':13,50,700,707,720,728,737,752,755,768,802,841,854,1149 'aura-on':719 'aura-specif':727 'auraen':39,161,228,758 'auto':219 'auto-escap':218 'automat':773,909,1065 'avoid':354,564,585 'back':477 'background':305 'base':324,342 'behaviour':357 'bind':212,544,948,1096,1211 'boolean':578,1141 'bound':1063 'boundari':180 'brand':303 'break':1107 'broadcast':825,1151 'browser':978 'bubbl':532 'built':109,346 'built-in':108,345 'button':302,331,411,667,1119 'bypass':920 'c':301 'c.isaccessible':1035 'cach':98,130 'cacheabl':126,146,163 'calcul':994 'call':148,491,654,677 'cannot':159,820 'card':340 'case':81 'catch':153 'caus':595 'chang':137 'check':46,241,976,1190,1200 'checklist':969 'child':487,495,810,1148 'choos':69 'class':314 'click':669 'close':479 'code':79 'collect':999 'color':294,297,304 'colour':293,436,1100 'communic':31,168,482,811,849 'complex':115,196 'complianc':21 'compon':3,11,14,30,41,51,167,249,325,343,381,481,506,521,613,626,648,701,711,744,746,753,792,805,818,859,1077,1154 'component-by-compon':743 'compos':317,535 'comput':567 'concaten':1202 'connectedcallback':557 'consid':390 'context':717 'control':78,759,878,928,1027,1059,1067,1179,1193 'convey':443 'correct':466,650 'cover':18 'coverag':643 'cross':166 'cross-compon':165 'crud':101,112,1024 'crud/fls':231,767 'csrf':35,896,907,921,1171 'css':320,361,1110 'custom':319,360,1066,1111,1178,1192 'customev':497,500 'dark':350,368,1103 'dark-mod':1102 'data':26,65,72,96,198,207,503,634,691,780,879,1091,1161,1182 'database.query':945 'dataset':611 'datat':337 'declar':371,762,996,1187 'decoupl':176,848 'default':1166 'describ':420 'describedbi':462 'design':804 'detail':502 'develop':979 'direct':484 'dispatch':664 'display':658 'dml':142,156,565,1052 'document.queryselector':513 'dom':179,562 'done':372,391 'e.g':722 'effect':555 'el':224 'element':425,918 'emb':258 'embed':750 'emit':690 'enforc':36,203,230,766,771,1060,1073 'enter':433 'entir':828 'error':452,673,680,692 'escal':1197 'escap':220,435,787,870,1157,1167 'etc':341 'event':522,665,730,803,806,815,819,856,1144,1155 'eventnam':501 'everi':48,379,561,625 'exist':328 'expect':644 'explicit':239 'expos':537 'exposur':1183 'express':211,1097 'extend':723 'fail':678 'failur':1122 'fals':871,1158 'ff3366':295 'field':94,987,1032,1047,1064 'filter':119 'fire':134 'fix':1087 'flow':519,528,547 'fls':111,1023,1061,1070,1188 'fls/crud':37 'focus':463,469 'follow':89 'forc':724 'form':102,393,912 'framework':217 'get':941,954 'getparamet':940,953 'getrecord':92 'gql':188 'graph':184 'graphql':186 'guard':573,579,606,1132,1142 'handl':628 'hardcod':243,292,1098 'hasrend':1140 'heavi':566 'helper':783 'hex':1099 'html':917 'hybrid':839 'icon':409,448,1118 'icon-on':408 'id':247,260,934,959 'imper':149 'import':316 'includ':344 'infinit':582,1136 'inject':905,925,1209 'innerhtml':209,225,1088 'input':334,394,458,790 'insid':593,751 'instead':620,1156 'interact':424,630 'javascript':77,250,550,640 'jest':622,638 'jest.mock':696 'kb':975 'keyboard':431 'label':399,406,419,1116,1130 'larg':610,618,998 'legaci':733 'librari':517 'light':365 'lightn':9,95,172,323,330,333,336,339,507,844 'lightning-button':329 'lightning-card':338 'lightning-datat':335 'lightning-input':332 'list':120,943,956 'lms':175,510 'logic':800 'loop':584,1138 'lowest':812 'lwc':12,49,61,380,709,713,739,741,747,840,858,1092,1135 'lwc/aura':1101 'lwc/aura/vf':1120 'mainten':836 'manag':464,734,968 'manual':1074 'mean':441 'mechan':485 'messag':173,453,508,845 'method':128,158,229,494,656,694,760 'migrat':736,742 'minimum':641 'miss':1113 'mock':662,684,688,695 'modal':468,473 'mode':235,351,366,369,1104 'move':470 'multi':182 'multi-object':181 'must':382,530,635,761,883,1071 'mutat':571 'name':938,942,955,963 'nameparam':951,964 'navig':90 'necessari':604 'need':524 'never':221,257,291,311,400,511,774,873,930 'new':499,710,1037,1056 'non':145 'non-cach':144 'null':1049 'object':106,183 'open':475 'oper':429 'org':245 'org-specif':244 'output':894 'overrid':312 'packag':735 'page':17,54,1015 'pagin':615 'pair':445 'param':123,136,151,1205 'paramet':284 'parent':171,266,271,486,496,809,1147 'parent-child':808,1146 'pass':253,273,383 'path':681 'pattern':28,67,74,82,1080,1084 'perform':551,834,1051 'persist':1002 'placehold':402 'platform':904 'postback':901,1004,1169 'prefer':740 'prevent':34,581,867,926 'privileg':1196 'problem':837 'prop':256 'properti':264,321,489,587,592,1112 'protect':897,922 'pub/sub':516 'qualiti':5,42,62 'queri':117,189,251,283 'quick':1075 'rang':278 'raw':205,916,1168 're':133,598 're-fir':132 're-rend':597 'reach':526,821,857 'reachabl':427 'reactiv':88,99,586,591 'read':84,1013,1030 'read-on':1012 'readon':1006 'record':87,259 'recordid':93 'refer':1076 'reimplement':355 'relat':197 'relationship':185 'reli':852 'remov':1164 'render':569,583,599,646,885 'renderedcallback':574,594,1134 'requir':154,375,624 'rerend':1137 'result':619,659,944,957 'retriev':632 'return':1048 'review':58 'rich':881 'right':71 'risk':1086 'round':193 'round-trip':192 'rule':32,201,202,483,552,757,869 'run':559 'runtim':529 'salesforc':2,8,40,983 'salesforce-component-standard':1 'salesforce/apex/myclass.mymethod':697 'salesforce/sfdx-lwc-jest':683 'sanitis':886 'schema.sobjecttype':240,1199 'schema.sobjecttype.account.fields.revenue':1034 'schema.sobjecttype.account.isdeletable':1054 'scope':813,1152 'screen':520 'section':59,698,860 'secur':200,756,863 'select':29,68,933,958 'semant':308 'serialis':1021 'server':116,147,888,992 'server-sid':887,991 'servic':97,174,509,846 'set':531,589,1009 'setimmedi':689 'share':170,764,1176,1186 'show':672 'sibl':504 'side':554,889,993 'singl':86,105,191 'skill' 'skill-salesforce-component-standards' 'skip':1017 'slds':19,286,300,309,313,348,1105,1109 'slds-c-button-brand-color-background':299 'soql':237,798,924,932,946,1201,1208 'sourc':262 'source-github' 'space':434 'specif':246,729 'stack':842 'standard':4,6,43,63,100,290,702,864,1058 'state':570,614,674,967,971,985,1020 'status':444 'store':609 'stream':617 'string':931,950 'style':289 'summari':1081 'system.noaccessexception':1057 'tab':432,986 'target':716,823 'technolog':1085 'templat':215,1095 'test':362,623,639,642 'text':415,447,882,1126 'this.dispatchevent':498 'this.template.queryselector':223 'throw':1055 'titl':651 'token':310,908 'tool':980 'topic-agent-skills' 'topic-agents' 'topic-awesome' 'topic-custom-agents' 'topic-github-copilot' 'topic-hacktoberfest' 'topic-prompt-engineering' 'transient':997 'trap':588 'trigger':140 'trip':194 'true':127,164,533,536,1007 'two':542 'two-way':541 'type':276 'unbound':782 'unescap':778 'unless':714 'unnecessari':1150 'unnecessarili':1005 'unrel':505 'url':1204 'use':80,210,222,232,269,280,296,322,359,401,512,576,600,682,706,726,775,784,795,843,898,915,988,1094,1108,1153,1165,1173,1210 'user':139,206,234,629,779,877,1090,1160 'user-control':876 'user-trigg':138 'uservalu':226 'util':685 'v.something':776 'valid':267,275,788 'valu':539 'var':298 'variabl':190,548,949,1212 'vf':1180,1194,1206 'via':459 'view':966,970,984,1019 'view-stat':1018 'visualforc':16,53,862,1026,1162,1170 'vs':708 'vulner':1172 'way':543 'wcag':23,376 'web':10 'wherev':326 'whitelist':892 'window':514 'wire':91,121,131,157,187,661,686 'without':162 'work':177 'write':56,76 'xml':868 'xss':33,866,1093,1163","prices":[{"id":"091b441c-b9dd-4eca-a616-9cf1dc7159e2","listingId":"b969af37-43e1-4362-bbe1-c3773161c997","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"github","category":"awesome-copilot","install_from":"skills.sh"},"createdAt":"2026-04-18T21:51:10.624Z"}],"sources":[{"listingId":"b969af37-43e1-4362-bbe1-c3773161c997","source":"github","sourceId":"github/awesome-copilot/salesforce-component-standards","sourceUrl":"https://github.com/github/awesome-copilot/tree/main/skills/salesforce-component-standards","isPrimary":false,"firstSeenAt":"2026-04-18T21:51:10.624Z","lastSeenAt":"2026-04-22T00:52:17.194Z"}],"details":{"listingId":"b969af37-43e1-4362-bbe1-c3773161c997","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"github","slug":"salesforce-component-standards","github":{"repo":"github/awesome-copilot","stars":30743,"topics":["agent-skills","agents","ai","awesome","custom-agents","github-copilot","hacktoberfest","prompt-engineering"],"license":"mit","html_url":"https://github.com/github/awesome-copilot","pushed_at":"2026-04-21T22:20:21Z","description":"Community-contributed instructions, agents, skills, and configurations to help you make the most of GitHub Copilot.","skill_md_sha":"87fd9c7632e7141ca02747ec20bba38854bebae1","skill_md_path":"skills/salesforce-component-standards/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/github/awesome-copilot/tree/main/skills/salesforce-component-standards"},"layout":"multi","source":"github","category":"awesome-copilot","frontmatter":{"name":"salesforce-component-standards","description":"Quality standards for Salesforce Lightning Web Components (LWC), Aura components, and Visualforce pages. Covers SLDS 2 compliance, accessibility (WCAG 2.1 AA), data access pattern selection, component communication rules, XSS prevention, CSRF enforcement, FLS/CRUD in AuraEnabled methods, view state management, and Jest test requirements. Use this skill when building or reviewing any Salesforce UI component to enforce platform-specific security and quality standards."},"skills_sh_url":"https://skills.sh/github/awesome-copilot/salesforce-component-standards"},"updatedAt":"2026-04-22T00:52:17.194Z"}}