@atezer/figma-mcp-bridge 1.7.23 → 1.7.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/.claude-plugin/plugin.json +37 -0
  2. package/.cursor-plugin/plugin.json +21 -0
  3. package/CHANGELOG.md +30 -0
  4. package/README.md +4 -3
  5. package/agents/ds-auditor.md +29 -0
  6. package/agents/screen-builder.md +29 -0
  7. package/agents/token-syncer.md +26 -0
  8. package/assets/logo.png +0 -0
  9. package/commands/add-library.md +122 -0
  10. package/commands/ds-add.md +255 -0
  11. package/commands/ds-sync.md +314 -0
  12. package/commands/implement.md +43 -0
  13. package/commands/install-library.md +73 -0
  14. package/commands/setup.md +26 -0
  15. package/commands/test.md +39 -0
  16. package/commands/update.md +25 -0
  17. package/dist/core/config.d.ts +1 -5
  18. package/dist/core/config.d.ts.map +1 -1
  19. package/dist/core/config.js +11 -111
  20. package/dist/core/config.js.map +1 -1
  21. package/dist/core/plugin-bridge-server.d.ts.map +1 -1
  22. package/dist/core/plugin-bridge-server.js +1 -2
  23. package/dist/core/plugin-bridge-server.js.map +1 -1
  24. package/dist/core/response-guard.d.ts +1 -1
  25. package/dist/core/response-guard.js +1 -1
  26. package/dist/core/types/index.d.ts +2 -98
  27. package/dist/core/types/index.d.ts.map +1 -1
  28. package/dist/core/version.d.ts +1 -1
  29. package/dist/core/version.js +1 -1
  30. package/dist/local-plugin-only.d.ts.map +1 -1
  31. package/dist/local-plugin-only.js +14 -13
  32. package/dist/local-plugin-only.js.map +1 -1
  33. package/f-mcp-plugin/README.md +8 -15
  34. package/f-mcp-plugin/manifest.json +1 -3
  35. package/hooks/hooks.json +26 -0
  36. package/package.json +15 -31
  37. package/skills/BRAND_PROFILE_SCHEMA.md +113 -0
  38. package/skills/SKILL_INDEX.md +194 -0
  39. package/skills/TOOL_MAPPING.md +111 -0
  40. package/skills/ai-handoff-export/SKILL.md +254 -0
  41. package/skills/apply-figma-design-system/SKILL.md +104 -0
  42. package/skills/audit-figma-design-system/SKILL.md +278 -0
  43. package/skills/code-design-mapper/SKILL.md +370 -0
  44. package/skills/component-documentation/SKILL.md +190 -0
  45. package/skills/design-drift-detector/SKILL.md +407 -0
  46. package/skills/design-system-rules/SKILL.md +407 -0
  47. package/skills/design-token-pipeline/SKILL.md +619 -0
  48. package/skills/ds-impact-analysis/SKILL.md +266 -0
  49. package/skills/figjam-diagram-builder/SKILL.md +172 -0
  50. package/skills/figma-a11y-audit/SKILL.md +587 -0
  51. package/skills/figma-canvas-ops/SKILL.md +325 -0
  52. package/skills/figma-screen-analyzer/SKILL.md +235 -0
  53. package/skills/fix-figma-design-system-finding/SKILL.md +117 -0
  54. package/skills/fmcp-project-rules/SKILL.md +93 -0
  55. package/skills/generate-figma-library/SKILL.md +598 -0
  56. package/skills/generate-figma-screen/SKILL.md +689 -0
  57. package/skills/implement-design/SKILL.md +473 -0
  58. package/skills/ux-copy-guidance/SKILL.md +373 -0
  59. package/skills/visual-qa-compare/SKILL.md +166 -0
  60. package/dist/browser/base.d.ts +0 -50
  61. package/dist/browser/base.d.ts.map +0 -1
  62. package/dist/browser/base.js +0 -6
  63. package/dist/browser/base.js.map +0 -1
  64. package/dist/browser/local.d.ts +0 -81
  65. package/dist/browser/local.d.ts.map +0 -1
  66. package/dist/browser/local.js +0 -283
  67. package/dist/browser/local.js.map +0 -1
  68. package/dist/core/console-monitor.d.ts +0 -82
  69. package/dist/core/console-monitor.d.ts.map +0 -1
  70. package/dist/core/console-monitor.js +0 -428
  71. package/dist/core/console-monitor.js.map +0 -1
  72. package/dist/core/design-system-manifest.d.ts +0 -272
  73. package/dist/core/design-system-manifest.d.ts.map +0 -1
  74. package/dist/core/design-system-manifest.js +0 -261
  75. package/dist/core/design-system-manifest.js.map +0 -1
  76. package/dist/core/enrichment/enrichment-service.d.ts +0 -52
  77. package/dist/core/enrichment/enrichment-service.d.ts.map +0 -1
  78. package/dist/core/enrichment/enrichment-service.js +0 -272
  79. package/dist/core/enrichment/enrichment-service.js.map +0 -1
  80. package/dist/core/enrichment/index.d.ts +0 -8
  81. package/dist/core/enrichment/index.d.ts.map +0 -1
  82. package/dist/core/enrichment/index.js +0 -8
  83. package/dist/core/enrichment/index.js.map +0 -1
  84. package/dist/core/enrichment/relationship-mapper.d.ts +0 -106
  85. package/dist/core/enrichment/relationship-mapper.d.ts.map +0 -1
  86. package/dist/core/enrichment/relationship-mapper.js +0 -352
  87. package/dist/core/enrichment/relationship-mapper.js.map +0 -1
  88. package/dist/core/enrichment/style-resolver.d.ts +0 -80
  89. package/dist/core/enrichment/style-resolver.d.ts.map +0 -1
  90. package/dist/core/enrichment/style-resolver.js +0 -327
  91. package/dist/core/enrichment/style-resolver.js.map +0 -1
  92. package/dist/core/figma-api.d.ts +0 -137
  93. package/dist/core/figma-api.d.ts.map +0 -1
  94. package/dist/core/figma-api.js +0 -274
  95. package/dist/core/figma-api.js.map +0 -1
  96. package/dist/core/figma-desktop-connector.d.ts +0 -242
  97. package/dist/core/figma-desktop-connector.d.ts.map +0 -1
  98. package/dist/core/figma-desktop-connector.js +0 -1042
  99. package/dist/core/figma-desktop-connector.js.map +0 -1
  100. package/dist/core/figma-reconstruction-spec.d.ts +0 -162
  101. package/dist/core/figma-reconstruction-spec.d.ts.map +0 -1
  102. package/dist/core/figma-reconstruction-spec.js +0 -387
  103. package/dist/core/figma-reconstruction-spec.js.map +0 -1
  104. package/dist/core/figma-tools.d.ts +0 -21
  105. package/dist/core/figma-tools.d.ts.map +0 -1
  106. package/dist/core/figma-tools.js +0 -2920
  107. package/dist/core/figma-tools.js.map +0 -1
  108. package/dist/core/snippet-injector.d.ts +0 -24
  109. package/dist/core/snippet-injector.d.ts.map +0 -1
  110. package/dist/core/snippet-injector.js +0 -97
  111. package/dist/core/snippet-injector.js.map +0 -1
  112. package/dist/core/types/enriched.d.ts +0 -213
  113. package/dist/core/types/enriched.d.ts.map +0 -1
  114. package/dist/core/types/enriched.js +0 -6
  115. package/dist/core/types/enriched.js.map +0 -1
  116. package/dist/local.d.ts +0 -73
  117. package/dist/local.d.ts.map +0 -1
  118. package/dist/local.js +0 -2605
  119. package/dist/local.js.map +0 -1
@@ -0,0 +1,587 @@
1
+ ---
2
+ name: figma-a11y-audit
3
+ description: Figma ekranını erişilebilirlik açısından denetler. Renk kontrastı (WCAG AA/AAA), minimum dokunma hedefi, fokus sırası, metin boyutu ve platform-bazlı ekran okuyucu önerileri (VoiceOver, TalkBack, ARIA) üretir. "a11y audit", "erişilebilirlik kontrol", "kontrast kontrol", "accessibility check", "ekran okuyucu spec", "WCAG kontrol" ifadeleriyle tetiklenir. F-MCP Bridge plugin bağlantısı gerektirir.
4
+ metadata:
5
+ mcp-server: user-figma-mcp-bridge
6
+ personas:
7
+ - designops
8
+ - uidev
9
+ - designer
10
+ ---
11
+
12
+ # Figma A11y Audit — Erişilebilirlik Denetimi
13
+
14
+ > **Design Token Kuralı:** Bu skill'deki kod örneklerinde geçen font adları, renk kodları, piksel boyutları yalnızca FORMAT gösterimidir. Çalışma anında tüm design token değerleri (font, renk, boyut, spacing, radius, gölge) kayıtlı kütüphaneden (`figma_get_variables`, `figma_get_styles`) veya kullanıcıdan okunmalıdır. Hardcoded token değeri kullanma. Detay: `project-context.md` → "Design Token Kuralı".
15
+
16
+ ## Overview
17
+
18
+ Bu skill, Figma ekranını erişilebilirlik (a11y) standartlarına göre denetler. Platform-bazlı (iOS VoiceOver, Android TalkBack, Web ARIA) raporlar üretir. Topluluk "Uber a11y ekran okuyucu spesifikasyonları" örneğinden esinlenilmiştir.
19
+
20
+ **Okuma + Yazma** — Denetim sonuçlarını okur, isteğe bağlı olarak annotation frame ekler.
21
+
22
+ ## Prerequisites
23
+
24
+ - F-MCP Bridge plugin bağlı olmalı
25
+ - Hedef ekranın node ID'si veya URL'i bilinmeli
26
+
27
+ ## F-MCP skill koordinasyonu
28
+
29
+ - **Önce (isteğe bağlı):** `audit-figma-design-system` (DS uyum denetimi) — a11y audit'ten bağımsız ama birlikte değerli
30
+ - **Sonra:** Bulgular varsa `fix-figma-design-system-finding` veya `apply-figma-design-system` ile düzeltme; kod tarafında `implement-design` çıktısında a11y attribute'ları
31
+ - **İlişkili:** `generate-figma-library` Faz 4 (a11y denetimi) bu skill'i referans alır
32
+
33
+ ## WCAG 2.1/2.2 AA Hızlı Referans
34
+
35
+ Denetim sırasında bu 4 prensip ve 12 kriter referans alınmalıdır:
36
+
37
+ ### Algılanabilir (Perceivable)
38
+ - **1.1.1 Metin Dışı İçerik:** Görseller, ikonlar ve dekoratif öğeler için alternatif metin veya `aria-hidden`
39
+ - **1.3.1 Bilgi ve İlişkiler:** Başlık hiyerarşisi (H1→H2→H3), form etiketleri, tablo başlıkları programatik olarak belirlenmeli
40
+ - **1.4.3 Kontrast (Minimum):** Normal metin ≥4.5:1, büyük metin (≥18px veya ≥14px bold) ≥3:1
41
+ - **1.4.11 Metin Dışı Kontrast:** UI bileşenleri (buton kenarları, input border, ikon) ve grafik öğeleri ≥3:1 — sadece metin değil!
42
+
43
+ ### İşletilebilir (Operable)
44
+ - **2.1.1 Klavye:** Tüm işlevsellik klavyeyle erişilebilir olmalı
45
+ - **2.4.3 Odak Sırası:** Fokus sırası mantıksal ve anlamlı olmalı (görsel sırayla tutarlı)
46
+ - **2.4.7 Görünür Odak:** Klavye fokus göstergesi açıkça görünür olmalı (ör. 2px solid ring, minimum 3:1 kontrast)
47
+ - **2.5.5 Dokunma Hedefi (WCAG 2.2):** Tıklanabilir/dokunulabilir öğeler ≥44×44 CSS px (WCAG 2.2: ≥24×24 CSS px minimum)
48
+
49
+ ### Anlaşılabilir (Understandable)
50
+ - **3.1.1 Sayfa Dili:** İçerik dili tanımlanmalı (lang attribute)
51
+ - **3.2.1 Fokusta Tahmin Edilebilirlik:** Bir öğeye odaklanmak beklenmeyen davranış tetiklememeli (ör. otomatik form submit)
52
+ - **3.3.1 Hata Tanımlama:** Hata tespit edildiğinde kullanıcıya metin olarak açıkça bildirilmeli
53
+ - **3.3.2 Etiketler veya Talimatlar:** Form alanlarında açık etiket ve gerektiğinde yardım metni
54
+
55
+ ### Sağlam (Robust)
56
+ - **4.1.2 Ad, Rol, Değer:** Tüm UI bileşenlerinin adı (label), rolü (role) ve durumu (state) programatik olarak belirlenebilir olmalı
57
+
58
+ ## Yaygın Sorunlar Kontrol Listesi
59
+
60
+ Denetimde sıkça karşılaşılan 8 sorun — her biri Step 4-8'de kontrol edilmelidir:
61
+
62
+ 1. **Yetersiz renk kontrastı** — metin/arka plan oranı WCAG AA'yı karşılamıyor
63
+ 2. **Eksik form etiketleri** — placeholder tek başına etiket yerine geçmez
64
+ 3. **Klavye erişimi yok** — fare olmadan ulaşılamayan etkileşimli öğeler
65
+ 4. **Alt text eksik** — bilgi taşıyan görsellerde alternatif metin yok
66
+ 5. **Focus trap hatalı** — modal/drawer'da odak tuzağı eksik veya bozuk
67
+ 6. **ARIA landmark eksik** — sayfa bölümleri (nav, main, footer) işaretlenmemiş
68
+ 7. **Otomatik oynatılan medya** — ses/video otomatik başlıyor, durdurma kontrolü yok
69
+ 8. **Zaman limitleri** — oturum zaman aşımı uzatma seçeneği sunulmuyor
70
+
71
+ ## Test Yaklaşımı Sırası
72
+
73
+ A11y denetimi aşağıdaki sırayla yürütülmelidir:
74
+
75
+ | Aşama | Ne Yapılır | İlgili Step |
76
+ |-------|-----------|-------------|
77
+ | 1. Otomatik tarama | Kontrast hesaplama, dokunma hedefi boyutu, metin boyutu | Step 4, 5, 6 |
78
+ | 2. Klavye-only navigasyon | Fokus sırası, görünür fokus göstergesi, focus trap | Step 7c, 7e |
79
+ | 3. Ekran okuyucu testi | Başlık hiyerarşisi, form ilişkilendirme, alt text | Step 7a, 7b, 7d |
80
+ | 4. Kontrast doğrulama | Gradient/overlay kaçırılanlar, UI bileşen kontrastı | Screenshot kontrolü |
81
+ | 5. %200 zoom testi | İçerik kaybı, yatay kaydırma oluşmamalı | Responsive kontrol |
82
+
83
+ ## Required Workflow
84
+
85
+ ### Step 1: Plugin Bağlantısını Doğrula
86
+
87
+ ```
88
+ figma_get_status()
89
+ ```
90
+
91
+ ### Step 2: Hedef Ekranı Belirle
92
+
93
+ Figma URL veya node ID. `node-id=72-293` → `72:293` normalize et.
94
+
95
+ ### Step 3: Yapı ve Görsel Veri Topla
96
+
97
+ ```
98
+ figma_get_design_context(
99
+ nodeId="<NODE_ID>",
100
+ depth=2,
101
+ verbosity="full",
102
+ includeLayout=true,
103
+ includeVisual=true,
104
+ includeTypography=true
105
+ )
106
+ ```
107
+
108
+ ```
109
+ figma_capture_screenshot(nodeId="<NODE_ID>")
110
+ ```
111
+
112
+ ```
113
+ figma_get_design_context(nodeId="<NODE_ID>", depth=3)
114
+ ```
115
+
116
+ ### Step 4: Kontrast Analizi
117
+
118
+ `figma_execute` ile renk çiftlerini çıkar ve kontrast oranı hesapla:
119
+
120
+ ```js
121
+ function luminance(r, g, b) {
122
+ const [rs, gs, bs] = [r, g, b].map(c =>
123
+ c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)
124
+ );
125
+ return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
126
+ }
127
+
128
+ function contrastRatio(l1, l2) {
129
+ const lighter = Math.max(l1, l2);
130
+ const darker = Math.min(l1, l2);
131
+ return (lighter + 0.05) / (darker + 0.05);
132
+ }
133
+
134
+ const root = await figma.getNodeByIdAsync("<NODE_ID>");
135
+ const textNodes = root.findAll(n => n.type === "TEXT");
136
+ const results = [];
137
+
138
+ for (const node of textNodes.slice(0, 50)) {
139
+ if (!node.fills || !node.fills.length) continue;
140
+ const fill = node.fills[0];
141
+ if (fill.type !== "SOLID") continue;
142
+
143
+ let parent = node.parent;
144
+ let bgColor = null;
145
+ while (parent && !bgColor) {
146
+ if (parent.fills && parent.fills.length) {
147
+ const pFill = parent.fills[0];
148
+ if (pFill.type === "SOLID") bgColor = pFill.color;
149
+ }
150
+ parent = parent.parent;
151
+ }
152
+ if (!bgColor) bgColor = { r: 1, g: 1, b: 1 };
153
+
154
+ const ratio = contrastRatio(
155
+ luminance(fill.color.r, fill.color.g, fill.color.b),
156
+ luminance(bgColor.r, bgColor.g, bgColor.b)
157
+ );
158
+
159
+ const fontSize = node.fontSize;
160
+ const isLarge = fontSize >= 18 || (fontSize >= 14 && node.fontWeight >= 700);
161
+ const passAA = isLarge ? ratio >= 3 : ratio >= 4.5;
162
+ const passAAA = isLarge ? ratio >= 4.5 : ratio >= 7;
163
+
164
+ if (!passAA) {
165
+ results.push({
166
+ nodeId: node.id,
167
+ name: node.name,
168
+ ratio: Math.round(ratio * 100) / 100,
169
+ required: isLarge ? 3 : 4.5,
170
+ level: "FAIL_AA",
171
+ fontSize
172
+ });
173
+ }
174
+ }
175
+
176
+ return { contrastIssues: results, totalTextNodes: textNodes.length };
177
+ ```
178
+
179
+ ### Step 5: Dokunma Hedefi Kontrolü (WCAG 2.5.5)
180
+
181
+ > **Referans:** WCAG 2.5.5 Target Size — iOS HIG: 44×44pt, Android Material: 48×48dp, Web WCAG 2.2: min 24×24 CSS px
182
+
183
+ ```js
184
+ const interactiveTypes = ["INSTANCE", "FRAME"];
185
+ const interactiveNames = /button|btn|link|input|toggle|switch|checkbox|radio|tab|chip/i;
186
+
187
+ const root = await figma.getNodeByIdAsync("<NODE_ID>");
188
+ const nodes = root.findAll(n =>
189
+ interactiveTypes.includes(n.type) && interactiveNames.test(n.name)
190
+ );
191
+
192
+ const issues = [];
193
+ for (const node of nodes) {
194
+ const w = node.width;
195
+ const h = node.height;
196
+ if (w < 44 || h < 44) {
197
+ issues.push({
198
+ nodeId: node.id,
199
+ name: node.name,
200
+ size: { width: Math.round(w), height: Math.round(h) },
201
+ minimumRequired: { ios: "44x44", android: "48x48" },
202
+ level: w < 24 || h < 24 ? "CRITICAL" : "WARNING"
203
+ });
204
+ }
205
+ }
206
+
207
+ return { touchTargetIssues: issues, totalInteractive: nodes.length };
208
+ ```
209
+
210
+ ### Step 6: Metin Boyutu Kontrolü
211
+
212
+ ```js
213
+ const root = await figma.getNodeByIdAsync("<NODE_ID>");
214
+ const textNodes = root.findAll(n => n.type === "TEXT");
215
+ const smallText = textNodes.filter(n => {
216
+ const size = typeof n.fontSize === "number" ? n.fontSize : 0;
217
+ return size > 0 && size < 12;
218
+ }).map(n => ({
219
+ nodeId: n.id,
220
+ name: n.name,
221
+ fontSize: n.fontSize,
222
+ recommendation: "Minimum 12px (body 14-16px önerilir)"
223
+ }));
224
+
225
+ return { smallTextIssues: smallText };
226
+ ```
227
+
228
+ ### Step 7: Erişilebilirlik Notları (Annotation) Üretimi
229
+
230
+ **Referans:** Indeed Figma Accessibility Annotation Kit yaklaşımı. Denetim sonuçlarına göre, Figma tasarımına geliştirici notları eklenir. Bu notlar tasarım dosyasında kalır ve geliştirici handoff'unda kritik bilgi sağlar.
231
+
232
+ `figma_execute` ile ekranın yanına annotation frame'i oluştur.
233
+
234
+ > **Font kuralı:** Annotation metinlerinde de DS fontunu kullan. Kayıtlı kütüphane varsa (`.claude/libraries/`) text style'lardan font ailesini oku. Bulunamazsa kullanıcıya sor. Kullanıcı "sen seç" derse `Inter` kullan. Aşağıdaki örnekte `FONT_FAMILY` kütüphaneden okunan font adıdır.
235
+
236
+ ```js
237
+ // FONT_FAMILY'yi kütüphaneden veya kullanıcıdan belirle
238
+ const FONT_FAMILY = "KütüphanedenOkunanFont";
239
+ await figma.loadFontAsync({ family: FONT_FAMILY, style: "Regular" });
240
+ await figma.loadFontAsync({ family: FONT_FAMILY, style: "Bold" });
241
+
242
+ const screen = await figma.getNodeByIdAsync("<NODE_ID>");
243
+
244
+ // Annotation frame — ekranın sağına yerleştir
245
+ const annotFrame = figma.createFrame();
246
+ annotFrame.name = "A11y Annotations";
247
+ annotFrame.layoutMode = "VERTICAL";
248
+ annotFrame.primaryAxisSizingMode = "AUTO";
249
+ annotFrame.counterAxisSizingMode = "AUTO";
250
+ annotFrame.itemSpacing = 16;
251
+ annotFrame.paddingLeft = 24;
252
+ annotFrame.paddingRight = 24;
253
+ annotFrame.paddingTop = 24;
254
+ annotFrame.paddingBottom = 24;
255
+ // Aşağıdaki renkler annotation UI'a özel değerlerdir, DS token'ı değildir — ancak DS'de karşılığı varsa oradan okunması tercih edilir
256
+ annotFrame.fills = [{ type: "SOLID", color: { r: 0.98, g: 0.95, b: 0.85 } }];
257
+ annotFrame.cornerRadius = 12;
258
+ annotFrame.x = screen.x + screen.width + 40;
259
+ annotFrame.y = screen.y;
260
+
261
+ // Helper: add annotation item
262
+ function addAnnotation(parent, emoji, title, body) {
263
+ const item = figma.createFrame();
264
+ item.name = "Annotation: " + title;
265
+ item.layoutMode = "VERTICAL";
266
+ item.primaryAxisSizingMode = "AUTO";
267
+ item.itemSpacing = 4;
268
+ item.fills = [];
269
+ parent.appendChild(item);
270
+ item.layoutSizingHorizontal = "FILL";
271
+
272
+ const heading = figma.createText();
273
+ heading.characters = emoji + " " + title;
274
+ heading.fontSize = 14;
275
+ heading.fontName = { family: FONT_FAMILY, style: "Bold" };
276
+ // Annotation metin renkleri — DS'de karşılığı varsa oradan okunması tercih edilir
277
+ heading.fills = [{ type: "SOLID", color: { r: 0.1, g: 0.1, b: 0.1 } }];
278
+ item.appendChild(heading);
279
+
280
+ const desc = figma.createText();
281
+ desc.characters = body;
282
+ desc.fontSize = 12;
283
+ desc.fontName = { family: FONT_FAMILY, style: "Regular" };
284
+ // Annotation açıklama rengi — DS'de karşılığı varsa oradan okunması tercih edilir
285
+ desc.fills = [{ type: "SOLID", color: { r: 0.3, g: 0.3, b: 0.3 } }];
286
+ item.appendChild(desc);
287
+ desc.layoutSizingHorizontal = "FILL";
288
+ }
289
+
290
+ return { annotFrameId: annotFrame.id };
291
+ ```
292
+
293
+ #### 7a. Gesture Erişilebilirlik Kontrolleri
294
+
295
+ Mobil ve dokunmatik arayüzlerde gesture-bazlı etkileşimler klavye/switch kullanıcıları için erişilebilir alternatifler gerektir:
296
+
297
+ | Gesture | Gerekli Alternatif | Platform Notu |
298
+ |---------|-------------------|---------------|
299
+ | Swipe (kaydırma) | Buton / ok kontrolü | iOS VoiceOver: swipe gesture'lar yeniden atanır |
300
+ | Long-press (uzun basma) | Context menu butonu | Android: `onLongClick` + `contentDescription` |
301
+ | Pinch (çimdikleme) | +/- zoom butonları | Web: `wheel` event alternatifi |
302
+ | Pull-to-refresh | Yenile butonu | Ekran okuyucu ile pull gesture kullanılamaz |
303
+ | Drag & drop | Taşı butonu / sıralama menüsü | Klavye ile sürükle-bırak erişilemez |
304
+
305
+ **Annotation:** Gesture kullanan öğeleri tespit et ve alternatif kontrolün mevcut olup olmadığını raporla:
306
+
307
+ ```
308
+ addAnnotation(annotFrame, "👆", "Gesture Kontrolü",
309
+ "Swipe left → Sil butonu mevcut mi? Long-press → Context menu alternatifi var mı?");
310
+ ```
311
+
312
+ #### 7b. Başlık Hiyerarşisi Notları
313
+
314
+ Ekrandaki metin elemanlarını analiz et ve başlık seviyelerini belirle. Geliştirici hangi metnin `<h1>`, `<h2>`, `<h3>` olduğunu bilmeli:
315
+
316
+ ```
317
+ Başlık Hiyerarşisi:
318
+ H1: "Hoş Geldiniz" (24px, Semi Bold) — Sayfa ana başlığı
319
+ H2: "Hesabınıza giriş yapın" (16px, Regular) — Alt başlık / açıklama
320
+ Body: Input placeholder, link, buton metinleri (14px)
321
+ ```
322
+
323
+ **Kural:** Ekranda yalnızca 1 adet H1 olmalı. Başlık seviyeleri atlanmamalı (H1 → H3 yanlış, H1 → H2 → H3 doğru).
324
+
325
+ #### 7c. Form Alan-Etiket İlişkilendirme Notları
326
+
327
+ Form alanları ile etiketlerinin programatik olarak ilişkilendirilmesi gerekir. Aksi takdirde ekran okuyucu kullanıcıları alanın ne için olduğunu anlayamaz:
328
+
329
+ ```
330
+ Form İlişkilendirme:
331
+ Input "E-posta adresi":
332
+ - label: "E-posta adresi" (for/id ilişkisi)
333
+ - type: email
334
+ - autocomplete: email
335
+ - required: true
336
+ - error: "Geçerli bir e-posta girin" (aria-describedby)
337
+
338
+ Input "Şifre":
339
+ - label: "Şifre" (for/id ilişkisi)
340
+ - type: password
341
+ - autocomplete: current-password
342
+ - required: true
343
+ - toggle: "Şifreyi göster/gizle" butonu (aria-label)
344
+ ```
345
+
346
+ **Kural:** Her form alanının bir `<label>` ile ilişkilendirilmesi **zorunludur**. Placeholder tek başına etiket yerine geçmez.
347
+
348
+ #### 7d. Odak Sırası (Focus Order) Notları
349
+
350
+ Ekrandaki etkileşimli öğelerin odak sırası görsel sıradan farklı olabilir. Bu notlar geliştiriciye doğru `tabindex` sırasını bildirir:
351
+
352
+ ```
353
+ Odak Sırası:
354
+ 1. "Hoş Geldiniz" (başlık — ekran okuyucu ilk buraya odaklanır)
355
+ 2. "E-posta adresi" input
356
+ 3. "Şifre" input
357
+ 4. "Giriş Yap" butonu
358
+ 5. "Şifremi unuttum" link
359
+ 6. "Google ile Giriş Yap" butonu
360
+ 7. "Kayıt Ol" link
361
+ ```
362
+
363
+ **Kural:** Mantıksal akış yukarıdan aşağıya olmalı. Görsel olarak yan yana olan elemanlar (ör. iş ilanı + favori ikonu) için sıra açıkça belirtilmeli.
364
+
365
+ #### 7e. Görsel Alternatif Metin Notları
366
+
367
+ Her görsel için alt text veya dekoratif işaretleme:
368
+
369
+ ```
370
+ Görsel Notları:
371
+ Logo (Ellipse): alt="MyApp logosu" (bilgi taşıyan görsel)
372
+ Divider çizgileri: role="presentation" (dekoratif — ekran okuyucudan gizle)
373
+ ```
374
+
375
+ **Kural:** İçerik taşıyan görseller `alt` text almalı. Sadece estetik amaçlı görseller `role="presentation"` veya `aria-hidden="true"` ile gizlenmeli.
376
+
377
+ #### 7f. Modal/Dialog ve Dinamik İçerik Notları
378
+
379
+ Eğer ekranda modal, toast, alert gibi dinamik öğeler varsa:
380
+
381
+ ```
382
+ Dinamik İçerik Notları:
383
+ - Modal açıldığında: odak modal'a taşınmalı (focus trap)
384
+ - Modal kapandığında: odak tetikleyen elemana dönmeli
385
+ - Toast/alert: role="alert" veya aria-live="polite"
386
+ - Loading: aria-busy="true", tamamlanınca aria-busy="false"
387
+ ```
388
+
389
+ **Kural:** Dinamik içerik değişiklikleri `aria-live` region ile duyurulmalı. Modal'larda fokus tuzağı (focus trap) **zorunludur**.
390
+
391
+ ### Step 8: Erişebilirlik-Tasarım Tutarlılık Kontrolü
392
+
393
+ Annotation'lar üretildikten sonra, gerçek tasarımın erişebilirlik kurallarıyla tutarlı olduğunu doğrula. `figma_execute` ile otomatik kontrol:
394
+
395
+ ```js
396
+ const screen = await figma.getNodeByIdAsync("<NODE_ID>");
397
+ const checks = [];
398
+
399
+ // 1. Başlık hiyerarşisi — max 1 adet H1 (>= 24px)
400
+ const allText = screen.findAll(n => n.type === "TEXT");
401
+ const h1Count = allText.filter(n => n.fontSize >= 24).length;
402
+ checks.push({ rule: "Single H1", pass: h1Count <= 1 });
403
+
404
+ // 2. Min font size 12px
405
+ const tooSmall = allText.filter(n => typeof n.fontSize === "number" && n.fontSize < 12);
406
+ checks.push({ rule: "Min font 12px", pass: tooSmall.length === 0 });
407
+
408
+ // 3. Body text min 14px
409
+ const bodySmall = allText.filter(n => n.fontSize >= 12 && n.fontSize < 14);
410
+ checks.push({ rule: "Body min 14px", pass: bodySmall.length === 0 });
411
+
412
+ // 4. Touch target min 44px (iOS HIG)
413
+ const interactive = screen.findAll(n =>
414
+ n.type === "INSTANCE" || (n.type === "FRAME" && /input|button|btn/i.test(n.name))
415
+ );
416
+ const smallTargets = interactive.filter(n => n.width < 44 || n.height < 44);
417
+ checks.push({ rule: "Touch 44px", pass: smallTargets.length === 0 });
418
+
419
+ // 5. Input min height 48px (Android Material)
420
+ const inputs = screen.findAll(n => n.type === "FRAME" && /input/i.test(n.name));
421
+ const smallInputs = inputs.filter(n => n.height < 48);
422
+ checks.push({ rule: "Input 48px", pass: smallInputs.length === 0 });
423
+
424
+ // 6. Tüm renkler token-bound (hard-coded fill yok)
425
+ let hardCoded = 0;
426
+ function chk(node) {
427
+ if (node.name === "_spacer") return;
428
+ if (node.fills?.length > 0 && node.fills[0].type === "SOLID") {
429
+ if (!node.boundVariables?.fills) hardCoded++;
430
+ }
431
+ if (node.children && node.type !== "INSTANCE") node.children.forEach(c => chk(c));
432
+ }
433
+ chk(screen);
434
+ checks.push({ rule: "Colors token-bound", pass: hardCoded === 0 });
435
+
436
+ // 7. Auto-layout (responsive) — tüm content frame'ler
437
+ const noLayout = screen.findAll(n => n.type === "FRAME" && n.name !== "_spacer" && (!n.layoutMode || n.layoutMode === "NONE"));
438
+ checks.push({ rule: "Auto-layout", pass: noLayout.length === 0 });
439
+
440
+ // 8. Fokus göstergesi renk kontrastı — focus ring var mı ve görünür mü?
441
+ const focusable = screen.findAll(n =>
442
+ n.type === "INSTANCE" || (n.type === "FRAME" && /focus|focused/i.test(n.name))
443
+ );
444
+ const hasFocusState = focusable.some(n => /focus/i.test(n.name));
445
+ checks.push({ rule: "Focus indicator", pass: hasFocusState || focusable.length === 0 });
446
+
447
+ // 9. Hata mesajı ilişkilendirme — error text + input yakınlığı
448
+ const errorTexts = allText.filter(n => /error|hata|geçersiz|invalid/i.test(n.name));
449
+ checks.push({ rule: "Error association", pass: true, note: `${errorTexts.length} hata metni tespit edildi — aria-describedby ilişkisi geliştiriciye bildirilmeli` });
450
+
451
+ // 10. UI bileşen kontrastı (1.4.11) — buton/input border rengi arka plana karşı ≥3:1
452
+ checks.push({ rule: "Non-text contrast 3:1", pass: true, note: "Manuel kontrol gerekli — buton/input kenar renkleri ve ikon kontrastı screenshot ile doğrulanmalı" });
453
+
454
+ return { pass: checks.filter(c => c.pass).length, total: checks.length, checks };
455
+ ```
456
+
457
+ **Tüm kontroller PASS olmalı.** FAIL varsa, ilgili bulguyu düzelt ve tekrar çalıştır.
458
+
459
+ ### Step 9: Platform-Bazlı Rapor Üret
460
+
461
+ #### iOS (VoiceOver) Raporu
462
+
463
+ ```markdown
464
+ ### VoiceOver Spesifikasyonu
465
+
466
+ | Öğe | accessibilityLabel | accessibilityTraits | Sıra |
467
+ |---|---|---|---|
468
+ | Logo | "Uygulama logosu" | .image | 1 |
469
+ | Başlık | "Hoş geldiniz" | .header | 2 |
470
+ | E-posta | "E-posta adresi" | .none (textField) | 3 |
471
+ | Giriş butonu | "Giriş yap" | .button | 4 |
472
+ ```
473
+
474
+ #### Android (TalkBack) Raporu
475
+
476
+ ```markdown
477
+ ### TalkBack Spesifikasyonu
478
+
479
+ | Öğe | contentDescription | Role | importantForAccessibility |
480
+ |---|---|---|---|
481
+ | Logo | "Uygulama logosu" | IMAGE | yes |
482
+ | Başlık | "Hoş geldiniz" | HEADING | yes |
483
+ | E-posta | "E-posta adresi" | EDIT_TEXT | yes |
484
+ | Giriş butonu | "Giriş yap" | BUTTON | yes |
485
+ ```
486
+
487
+ #### Web (ARIA) Raporu
488
+
489
+ ```markdown
490
+ ### ARIA Spesifikasyonu
491
+
492
+ | Öğe | role | aria-label | tabindex |
493
+ |---|---|---|---|
494
+ | Logo | img | "Uygulama logosu" | -1 |
495
+ | Başlık | heading (h1) | — | -1 |
496
+ | E-posta | textbox | "E-posta adresi" | 0 |
497
+ | Giriş butonu | button | "Giriş yap" | 0 |
498
+ ```
499
+
500
+ ## Türkçe Karakter Kuralı (ZORUNLU)
501
+
502
+ Tüm Türkçe metin içeriklerinde (Figma text node, kod string, dokümantasyon) doğru Unicode karakterler kullanılmalıdır. ASCII karşılıkları YASAKTIR:
503
+
504
+ | Doğru | Yanlış | Doğru | Yanlış |
505
+ |-------|--------|-------|--------|
506
+ | ş | s | Ş | S |
507
+ | ı | i | İ | I |
508
+ | ö | o | Ö | O |
509
+ | ü | u | Ü | U |
510
+ | ç | c | Ç | C |
511
+ | ğ | g | Ğ | G |
512
+
513
+ Son adım: Üretilen tüm Türkçe metinleri karakter kontrolünden geçir.
514
+
515
+ ## Çıktı Formatı
516
+
517
+ - **Varsayılan:** Markdown rapor (tüm platformlar)
518
+ - **`--json`:** Yapılandırılmış JSON (CI entegrasyonu için)
519
+ - **`--platform=ios`:** Yalnızca VoiceOver raporu
520
+ - **`--platform=android`:** Yalnızca TalkBack raporu
521
+ - **`--platform=web`:** Yalnızca ARIA raporu
522
+
523
+ ## Bulgu Öncelikleri
524
+
525
+ | Seviye | Açıklama | Örnek |
526
+ |---|---|---|
527
+ | **CRITICAL** | WCAG AA başarısız, kullanılamaz | Kontrast < 2:1, touch target < 24px |
528
+ | **HIGH** | WCAG AA sınırda | Kontrast 3-4.5:1 normal metin |
529
+ | **MEDIUM** | Best practice ihlali | Touch target 24-44px, metin < 14px |
530
+ | **LOW** | İyileştirme önerisi | AAA başarısız ama AA geçiyor |
531
+
532
+ ## Annotation Çıktı Formatı
533
+
534
+ Skill çalıştırıldığında, Figma dosyasında ekranın yanına sarı bir annotation frame oluşturulur:
535
+
536
+ ```
537
+ ┌──────────────────────────────────────┐
538
+ │ A11y Annotations │
539
+ │ │
540
+ │ 🏷️ Başlık Hiyerarşisi │
541
+ │ H1: "Hoş Geldiniz" │
542
+ │ H2: "Hesabınıza giriş yapın" │
543
+ │ │
544
+ │ 🔗 Form İlişkilendirme │
545
+ │ "E-posta" → label + type=email │
546
+ │ "Şifre" → label + type=password │
547
+ │ │
548
+ │ 🔢 Odak Sırası │
549
+ │ 1→Başlık 2→Email 3→Şifre 4→Giriş │
550
+ │ 5→Şifremi unuttum 6→Google 7→Kayıt │
551
+ │ │
552
+ │ 🖼️ Görsel Alt Text │
553
+ │ Logo: alt="MyApp logosu" │
554
+ │ Divider: decorative (gizle) │
555
+ │ │
556
+ │ ⚡ Kontrast Sonuçları │
557
+ │ Primary: 5.17:1 ✅ AA │
558
+ │ Secondary: 16.13:1 ✅ AA+AAA │
559
+ │ Disabled: 4.39:1 ℹ️ Muaf │
560
+ │ │
561
+ │ 📐 Touch Target │
562
+ │ Tüm butonlar ≥44px ✅ │
563
+ │ Tüm inputlar ≥48px ✅ │
564
+ └──────────────────────────────────────┘
565
+ ```
566
+
567
+ Bu annotation frame'i Figma dosyasında kalır ve geliştirici handoff paketinin bir parçası olur. `ai-handoff-export` skill'i bu notları otomatik olarak HANDOFF.md'ye dahil eder.
568
+
569
+ ## Doğrulama Kontrol Listesi
570
+
571
+ Skill tamamlandığında şu kontroller yapılmalı:
572
+
573
+ - [ ] Tüm renk çiftleri WCAG AA kontrolünden geçti mi?
574
+ - [ ] Tüm interaktif elemanlar ≥44px (iOS) / ≥48px (Android) mu?
575
+ - [ ] Tüm metinler ≥12px mi? Body metinler ≥14px mi?
576
+ - [ ] Başlık hiyerarşisi doğru mu? (tek H1, seviye atlama yok)
577
+ - [ ] Form alanları etiketlerle ilişkilendirildi mi?
578
+ - [ ] Odak sırası mantıksal mı?
579
+ - [ ] Dekoratif görseller işaretlendi mi?
580
+ - [ ] Annotation frame oluşturuldu mu?
581
+ - [ ] Platform-bazlı rapor (VoiceOver/TalkBack/ARIA) üretildi mi?
582
+
583
+ ## Evolution Triggers
584
+
585
+ - WCAG 3.0 yayınlandığında kontrast algoritması (APCA) güncellenmeli
586
+ - Bridge'e a11y-spesifik araç eklenirse (ör. erişilebilirlik ağacı aracı) iş akışı güncellenmeli
587
+ - Yeni platform (Flutter, .NET MAUI) desteği eklenirse rapor şablonları genişletilmeli