# Figma A11y Audit

> 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.

- **Type:** Skill
- **Install:** `agentstack add skill-atezer-fmcp-figma-a11y-audit`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [atezer](https://agentstack.voostack.com/s/atezer)
- **Installs:** 0
- **Category:** [Content & Media](https://agentstack.voostack.com/c/content-and-media)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [atezer](https://github.com/atezer)
- **Source:** https://github.com/atezer/FMCP/tree/main/skills/figma-a11y-audit
- **Website:** https://www.npmjs.com/package/@atezer/figma-mcp-bridge

## Install

```sh
agentstack add skill-atezer-fmcp-figma-a11y-audit
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Figma A11y Audit — Erişilebilirlik Denetimi

> **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ı".

## Overview

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.

**Okuma + Yazma** — Denetim sonuçlarını okur, isteğe bağlı olarak annotation frame ekler.

## Prerequisites

- F-MCP Bridge plugin bağlı olmalı
- Hedef ekranın node ID'si veya URL'i bilinmeli

## F-MCP skill koordinasyonu

- **Önce (isteğe bağlı):** `audit-figma-design-system` (DS uyum denetimi) — a11y audit'ten bağımsız ama birlikte değerli
- **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ı
- **İlişkili:** `generate-figma-library` Faz 4 (a11y denetimi) bu skill'i referans alır

## WCAG 2.1/2.2 AA Hızlı Referans

Denetim sırasında bu 4 prensip ve 12 kriter referans alınmalıdır:

### Algılanabilir (Perceivable)
- **1.1.1 Metin Dışı İçerik:** Görseller, ikonlar ve dekoratif öğeler için alternatif metin veya `aria-hidden`
- **1.3.1 Bilgi ve İlişkiler:** Başlık hiyerarşisi (H1→H2→H3), form etiketleri, tablo başlıkları programatik olarak belirlenmeli
- **1.4.3 Kontrast (Minimum):** Normal metin ≥4.5:1, büyük metin (≥18px veya ≥14px bold) ≥3:1
- **1.4.11 Metin Dışı Kontrast:** UI bileşenleri (buton kenarları, input border, ikon) ve grafik öğeleri ≥3:1 — sadece metin değil!

### İşletilebilir (Operable)
- **2.1.1 Klavye:** Tüm işlevsellik klavyeyle erişilebilir olmalı
- **2.4.3 Odak Sırası:** Fokus sırası mantıksal ve anlamlı olmalı (görsel sırayla tutarlı)
- **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)
- **2.5.5 Dokunma Hedefi (WCAG 2.2):** Tıklanabilir/dokunulabilir öğeler ≥44×44 CSS px (WCAG 2.2: ≥24×24 CSS px minimum)

### Anlaşılabilir (Understandable)
- **3.1.1 Sayfa Dili:** İçerik dili tanımlanmalı (lang attribute)
- **3.2.1 Fokusta Tahmin Edilebilirlik:** Bir öğeye odaklanmak beklenmeyen davranış tetiklememeli (ör. otomatik form submit)
- **3.3.1 Hata Tanımlama:** Hata tespit edildiğinde kullanıcıya metin olarak açıkça bildirilmeli
- **3.3.2 Etiketler veya Talimatlar:** Form alanlarında açık etiket ve gerektiğinde yardım metni

### Sağlam (Robust)
- **4.1.2 Ad, Rol, Değer:** Tüm UI bileşenlerinin adı (label), rolü (role) ve durumu (state) programatik olarak belirlenebilir olmalı

## Yaygın Sorunlar Kontrol Listesi

Denetimde sıkça karşılaşılan 8 sorun — her biri Step 4-8'de kontrol edilmelidir:

1. **Yetersiz renk kontrastı** — metin/arka plan oranı WCAG AA'yı karşılamıyor
2. **Eksik form etiketleri** — placeholder tek başına etiket yerine geçmez
3. **Klavye erişimi yok** — fare olmadan ulaşılamayan etkileşimli öğeler
4. **Alt text eksik** — bilgi taşıyan görsellerde alternatif metin yok
5. **Focus trap hatalı** — modal/drawer'da odak tuzağı eksik veya bozuk
6. **ARIA landmark eksik** — sayfa bölümleri (nav, main, footer) işaretlenmemiş
7. **Otomatik oynatılan medya** — ses/video otomatik başlıyor, durdurma kontrolü yok
8. **Zaman limitleri** — oturum zaman aşımı uzatma seçeneği sunulmuyor

## Test Yaklaşımı Sırası

A11y denetimi aşağıdaki sırayla yürütülmelidir:

| Aşama | Ne Yapılır | İlgili Step |
|-------|-----------|-------------|
| 1. Otomatik tarama | Kontrast hesaplama, dokunma hedefi boyutu, metin boyutu | Step 4, 5, 6 |
| 2. Klavye-only navigasyon | Fokus sırası, görünür fokus göstergesi, focus trap | Step 7c, 7e |
| 3. Ekran okuyucu testi | Başlık hiyerarşisi, form ilişkilendirme, alt text | Step 7a, 7b, 7d |
| 4. Kontrast doğrulama | Gradient/overlay kaçırılanlar, UI bileşen kontrastı | Screenshot kontrolü |
| 5. %200 zoom testi | İçerik kaybı, yatay kaydırma oluşmamalı | Responsive kontrol |

## Required Workflow

### Step 1: Plugin Bağlantısını Doğrula

```
figma_get_status()
```

### Step 2: Hedef Ekranı Belirle

Figma URL veya node ID. `node-id=72-293` → `72:293` normalize et.

### Step 3: Yapı ve Görsel Veri Topla

```
figma_get_design_context(
  nodeId="",
  depth=2,
  verbosity="full",
  includeLayout=true,
  includeVisual=true,
  includeTypography=true
)
```

```
figma_capture_screenshot(nodeId="")
```

```
figma_get_design_context(nodeId="", depth=3)
```

### Step 4: Kontrast Analizi

`figma_execute` ile renk çiftlerini çıkar ve kontrast oranı hesapla:

```js
function luminance(r, g, b) {
  const [rs, gs, bs] = [r, g, b].map(c =>
    c ");
const textNodes = root.findAll(n => n.type === "TEXT");
const results = [];

for (const node of textNodes.slice(0, 50)) {
  if (!node.fills || !node.fills.length) continue;
  const fill = node.fills[0];
  if (fill.type !== "SOLID") continue;

  let parent = node.parent;
  let bgColor = null;
  while (parent && !bgColor) {
    if (parent.fills && parent.fills.length) {
      const pFill = parent.fills[0];
      if (pFill.type === "SOLID") bgColor = pFill.color;
    }
    parent = parent.parent;
  }
  if (!bgColor) bgColor = { r: 1, g: 1, b: 1 };

  const ratio = contrastRatio(
    luminance(fill.color.r, fill.color.g, fill.color.b),
    luminance(bgColor.r, bgColor.g, bgColor.b)
  );

  const fontSize = node.fontSize;
  const isLarge = fontSize >= 18 || (fontSize >= 14 && node.fontWeight >= 700);
  const passAA = isLarge ? ratio >= 3 : ratio >= 4.5;
  const passAAA = isLarge ? ratio >= 4.5 : ratio >= 7;

  if (!passAA) {
    results.push({
      nodeId: node.id,
      name: node.name,
      ratio: Math.round(ratio * 100) / 100,
      required: isLarge ? 3 : 4.5,
      level: "FAIL_AA",
      fontSize
    });
  }
}

return { contrastIssues: results, totalTextNodes: textNodes.length };
```

### Step 5: Dokunma Hedefi Kontrolü (WCAG 2.5.5)

> **Referans:** WCAG 2.5.5 Target Size — iOS HIG: 44×44pt, Android Material: 48×48dp, Web WCAG 2.2: min 24×24 CSS px

```js
const interactiveTypes = ["INSTANCE", "FRAME"];
const interactiveNames = /button|btn|link|input|toggle|switch|checkbox|radio|tab|chip/i;

const root = await figma.getNodeByIdAsync("");
const nodes = root.findAll(n =>
  interactiveTypes.includes(n.type) && interactiveNames.test(n.name)
);

const issues = [];
for (const node of nodes) {
  const w = node.width;
  const h = node.height;
  if (w ");
const textNodes = root.findAll(n => n.type === "TEXT");
const smallText = textNodes.filter(n => {
  const size = typeof n.fontSize === "number" ? n.fontSize : 0;
  return size > 0 && size  ({
  nodeId: n.id,
  name: n.name,
  fontSize: n.fontSize,
  recommendation: "Minimum 12px (body 14-16px önerilir)"
}));

return { smallTextIssues: smallText };
```

### Step 7: Erişilebilirlik Notları (Annotation) Üretimi

**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.

`figma_execute` ile ekranın yanına annotation frame'i oluştur.

> **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.

```js
// FONT_FAMILY'yi kütüphaneden veya kullanıcıdan belirle
const FONT_FAMILY = "KütüphanedenOkunanFont";
await figma.loadFontAsync({ family: FONT_FAMILY, style: "Regular" });
await figma.loadFontAsync({ family: FONT_FAMILY, style: "Bold" });

const screen = await figma.getNodeByIdAsync("");

// Annotation frame — ekranın sağına yerleştir
const annotFrame = figma.createFrame();
annotFrame.name = "A11y Annotations";
annotFrame.layoutMode = "VERTICAL";
annotFrame.primaryAxisSizingMode = "AUTO";
annotFrame.counterAxisSizingMode = "AUTO";
annotFrame.itemSpacing = 16;
annotFrame.paddingLeft = 24;
annotFrame.paddingRight = 24;
annotFrame.paddingTop = 24;
annotFrame.paddingBottom = 24;
// Aşağıdaki renkler annotation UI'a özel değerlerdir, DS token'ı değildir — ancak DS'de karşılığı varsa oradan okunması tercih edilir
annotFrame.fills = [{ type: "SOLID", color: { r: 0.98, g: 0.95, b: 0.85 } }];
annotFrame.cornerRadius = 12;
annotFrame.x = screen.x + screen.width + 40;
annotFrame.y = screen.y;

// Helper: add annotation item
function addAnnotation(parent, emoji, title, body) {
  const item = figma.createFrame();
  item.name = "Annotation: " + title;
  item.layoutMode = "VERTICAL";
  item.primaryAxisSizingMode = "AUTO";
  item.itemSpacing = 4;
  item.fills = [];
  parent.appendChild(item);
  item.layoutSizingHorizontal = "FILL";

  const heading = figma.createText();
  heading.characters = emoji + " " + title;
  heading.fontSize = 14;
  heading.fontName = { family: FONT_FAMILY, style: "Bold" };
  // Annotation metin renkleri — DS'de karşılığı varsa oradan okunması tercih edilir
  heading.fills = [{ type: "SOLID", color: { r: 0.1, g: 0.1, b: 0.1 } }];
  item.appendChild(heading);

  const desc = figma.createText();
  desc.characters = body;
  desc.fontSize = 12;
  desc.fontName = { family: FONT_FAMILY, style: "Regular" };
  // Annotation açıklama rengi — DS'de karşılığı varsa oradan okunması tercih edilir
  desc.fills = [{ type: "SOLID", color: { r: 0.3, g: 0.3, b: 0.3 } }];
  item.appendChild(desc);
  desc.layoutSizingHorizontal = "FILL";
}

return { annotFrameId: annotFrame.id };
```

#### 7a. Gesture Erişilebilirlik Kontrolleri

Mobil ve dokunmatik arayüzlerde gesture-bazlı etkileşimler klavye/switch kullanıcıları için erişilebilir alternatifler gerektir:

| Gesture | Gerekli Alternatif | Platform Notu |
|---------|-------------------|---------------|
| Swipe (kaydırma) | Buton / ok kontrolü | iOS VoiceOver: swipe gesture'lar yeniden atanır |
| Long-press (uzun basma) | Context menu butonu | Android: `onLongClick` + `contentDescription` |
| Pinch (çimdikleme) | +/- zoom butonları | Web: `wheel` event alternatifi |
| Pull-to-refresh | Yenile butonu | Ekran okuyucu ile pull gesture kullanılamaz |
| Drag & drop | Taşı butonu / sıralama menüsü | Klavye ile sürükle-bırak erişilemez |

**Annotation:** Gesture kullanan öğeleri tespit et ve alternatif kontrolün mevcut olup olmadığını raporla:

```
addAnnotation(annotFrame, "👆", "Gesture Kontrolü",
  "Swipe left → Sil butonu mevcut mi? Long-press → Context menu alternatifi var mı?");
```

#### 7b. Başlık Hiyerarşisi Notları

Ekrandaki metin elemanlarını analiz et ve başlık seviyelerini belirle. Geliştirici hangi metnin ``, ``, `` olduğunu bilmeli:

```
Başlık Hiyerarşisi:
  H1: "Hoş Geldiniz" (24px, Semi Bold) — Sayfa ana başlığı
  H2: "Hesabınıza giriş yapın" (16px, Regular) — Alt başlık / açıklama
  Body: Input placeholder, link, buton metinleri (14px)
```

**Kural:** Ekranda yalnızca 1 adet H1 olmalı. Başlık seviyeleri atlanmamalı (H1 → H3 yanlış, H1 → H2 → H3 doğru).

#### 7c. Form Alan-Etiket İlişkilendirme Notları

Form alanları ile etiketlerinin programatik olarak ilişkilendirilmesi gerekir. Aksi takdirde ekran okuyucu kullanıcıları alanın ne için olduğunu anlayamaz:

```
Form İlişkilendirme:
  Input "E-posta adresi":
    - label: "E-posta adresi" (for/id ilişkisi)
    - type: email
    - autocomplete: email
    - required: true
    - error: "Geçerli bir e-posta girin" (aria-describedby)

  Input "Şifre":
    - label: "Şifre" (for/id ilişkisi)
    - type: password
    - autocomplete: current-password
    - required: true
    - toggle: "Şifreyi göster/gizle" butonu (aria-label)
```

**Kural:** Her form alanının bir `` ile ilişkilendirilmesi **zorunludur**. Placeholder tek başına etiket yerine geçmez.

#### 7d. Odak Sırası (Focus Order) Notları

Ekrandaki etkileşimli öğelerin odak sırası görsel sıradan farklı olabilir. Bu notlar geliştiriciye doğru `tabindex` sırasını bildirir:

```
Odak Sırası:
  1. "Hoş Geldiniz" (başlık — ekran okuyucu ilk buraya odaklanır)
  2. "E-posta adresi" input
  3. "Şifre" input
  4. "Giriş Yap" butonu
  5. "Şifremi unuttum" link
  6. "Google ile Giriş Yap" butonu
  7. "Kayıt Ol" link
```

**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.

#### 7d.1. Prototip Odak Sırası Çapraz Kontrolü (v1.9.9+)

Ekranda prototip bağlantıları varsa, `figma_get_prototype_connections` ile tüm ON_CLICK reaction source'larını listele ve 7d'deki focus sırası ile karşılaştır:

```js
const proto = await figma_get_prototype_connections({ nodeId:  });
const clickableSources = proto.connections
  .filter(c => c.reactions.some(r => r.trigger?.type === "ON_CLICK"))
  .map(c => ({ id: c.nodeId, name: c.name }));
```

**Kontrol kuralı:**
- 7d'deki focus sırası listesi (ör. `[email, password, loginBtn, forgotLink, signupLink]`)
- `clickableSources` listesi (ör. `[loginBtn, signupLink, forgotLink]`)

Prototype'ta clickable olan ama a11y focus sırasında yer almayan veya sıralaması farklı olan elementler varsa **UYARI** ver:

```
⚠️ A11y focus sırası ile prototip etkileşim sırası uyuşmuyor.
   - Focus sırasında: forgotLink #4, signupLink #5
   - Prototype'ta tıklanabilir: signupLink (#5 ama prototipte önce geliyor)
   - Öneri: tabindex gözden geçir veya DOM sırasını değiştir.
```

Bu kontrol klavye-only kullanıcılar için kritik: prototype'ta "Giriş Yap" → "Kayıt Ol" beklenirken tabindex bunu sağlamıyorsa erişilebilirlik bozulur.

#### 7e. Görsel Alternatif Metin Notları

Her görsel için alt text veya dekoratif işaretleme:

```
Görsel Notları:
  Logo (Ellipse): alt="MyApp logosu" (bilgi taşıyan görsel)
  Divider çizgileri: role="presentation" (dekoratif — ekran okuyucudan gizle)
```

**Kural:** İçerik taşıyan görseller `alt` text almalı. Sadece estetik amaçlı görseller `role="presentation"` veya `aria-hidden="true"` ile gizlenmeli.

#### 7f. Modal/Dialog ve Dinamik İçerik Notları

Eğer ekranda modal, toast, alert gibi dinamik öğeler varsa:

```
Dinamik İçerik Notları:
  - Modal açıldığında: odak modal'a taşınmalı (focus trap)
  - Modal kapandığında: odak tetikleyen elemana dönmeli
  - Toast/alert: role="alert" veya aria-live="polite"
  - Loading: aria-busy="true", tamamlanınca aria-busy="false"
```

**Kural:** Dinamik içerik değişiklikleri `aria-live` region ile duyurulmalı. Modal'larda fokus tuzağı (focus trap) **zorunludur**.

### Step 8: Erişebilirlik-Tasarım Tutarlılık Kontrolü

Annotation'lar üretildikten sonra, gerçek tasarımın erişebilirlik kurallarıyla tutarlı olduğunu doğrula. `figma_execute` ile otomatik kontrol:

```js
const screen = await figma.getNodeByIdAsync("");
const checks = [];

// 1. Başlık hiyerarşisi — max 1 adet H1 (>= 24px)
const allText = screen.findAll(n => n.type === "TEXT");
const h1Count = allText.filter(n => n.fontSize >= 24).length;
checks.push({ rule: "Single H1", pass: h1Count  typeof n.fontSize === "number" && n.fontSize  n.fontSize >= 12 && n.fontSize 
  n.type === "INSTANCE" || (n.type === "FRAME" && /input|button|btn/i.test(n.name))
);
const smallTargets = interactive.filter(n => n.width  n.type === "FRAME" && /input/i.test(n.name));
const smallInputs = inputs.filter(n => n.height  0 && node.fills[0].type === "SOLID") {
    if (!node.boundVariables?.fills) hardCoded++;
  }
  if (node.children && node.type !== "INSTANCE") node.children.forEach(c => chk(c));
}
chk(screen);
checks.push({ rule: "Colors token-bound", pass: hardCoded === 0 });

// 7. Auto-layout (responsive) — tüm content frame'ler
const noLayout = screen.findAll(n => n.type === "FRAME" && n.name !== "_spacer" && (!n.layoutMode || n.layoutMode === "NONE"));
checks.push({ rule: "Auto-layout", pass: noLayout.length =

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [atezer](https://github.com/atezer)
- **Source:** [atezer/FMCP](https://github.com/atezer/FMCP)
- **License:** MIT
- **Homepage:** https://www.npmjs.com/package/@atezer/figma-mcp-bridge

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-atezer-fmcp-figma-a11y-audit
- Seller: https://agentstack.voostack.com/s/atezer
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
