رموز التصميم هي المفردات المشتركة بين المصممين والمطورين. وجدت هذه الرموز بأشكال مختلفة منذ عصر Salesforce Lightning — Style Dictionary وTheo ومسودة مواصفات مجموعة مجتمع رموز تصميم W3C. ما تغيّر مؤخرًا هو أنها باتت أيضًا المفردات المشتركة بين قاعدة الكود الخاصة بك ووكلاء الترميز بالذكاء الاصطناعي.
الوكيل الذي يعرف أن color.accent = #d96a3a سيستخدم تلك القيمة. أما الوكيل الذي يحصل على لقطة شاشة فسيخمّن "برتقالي دافئ" ويُنتج شيئًا قريبًا ولكن خاطئًا. والفرق يتضاعف عبر عشرات المكونات.
figmascope يُصدّر ملف tokens.json كجزء من كل حزمة سياق. تشرح هذه المقالة التنسيق بالتفصيل، وكيف تحصل figmascope على الرموز من Figma، والاستنتاج التلقائي للملفات التي لا تحتوي على Figma Variables، وكيفية ربط الناتج بأطر العمل الشائعة. لمعرفة كيف تندرج الرموز في سير عمل التصدير الكامل، تفضل بزيارة تطبيق figmascope أو اقرأ Figma إلى Cursor وFigma إلى Claude Code.
تنسيق tokens.json
تستخدم figmascope تنسيقًا مستوحى من مواصفة مجموعة مجتمع رموز تصميم W3C. كل رمز هو كائن يحتوي على حقلي $value و$type، متداخلان تحت مفاتيح فئات دلالية:
{
"color": {
"surface": { "$value": "#f6f2ea", "$type": "color" },
"surface-2": { "$value": "#efe9dc", "$type": "color" },
"ink": { "$value": "#1f1d1a", "$type": "color" },
"ink-muted": { "$value": "#4a4641", "$type": "color" },
"accent": { "$value": "#d96a3a", "$type": "color" },
"accent-soft": { "$value": "#f2c7a8", "$type": "color" },
"good": { "$value": "#6a8f5a", "$type": "color" },
"warn": { "$value": "#c89a3a", "$type": "color" },
"bad": { "$value": "#b8553a", "$type": "color" }
},
"spacing": {
"1": { "$value": "4px", "$type": "dimension" },
"2": { "$value": "8px", "$type": "dimension" },
"3": { "$value": "12px", "$type": "dimension" },
"4": { "$value": "16px", "$type": "dimension" },
"6": { "$value": "24px", "$type": "dimension" },
"8": { "$value": "32px", "$type": "dimension" },
"12": { "$value": "48px", "$type": "dimension" },
"16": { "$value": "64px", "$type": "dimension" }
},
"radius": {
"sm": { "$value": "4px", "$type": "dimension" },
"md": { "$value": "8px", "$type": "dimension" },
"lg": { "$value": "16px", "$type": "dimension" },
"full": { "$value": "9999px", "$type": "dimension" }
},
"typography": {
"body": {
"fontFamily": { "$value": "Inter", "$type": "fontFamily" },
"fontSize": { "$value": "14px", "$type": "dimension" },
"fontWeight": { "$value": 400, "$type": "number" },
"lineHeight": { "$value": 1.45, "$type": "number" }
},
"heading": {
"sm": {
"fontFamily": { "$value": "Inter", "$type": "fontFamily" },
"fontSize": { "$value": "18px", "$type": "dimension" },
"fontWeight": { "$value": 600, "$type": "number" },
"lineHeight": { "$value": 1.25, "$type": "number" }
}
},
"mono": {
"fontFamily": { "$value": "JetBrains Mono", "$type": "fontFamily" },
"fontSize": { "$value": "13px", "$type": "dimension" },
"fontWeight": { "$value": 400, "$type": "number" }
}
}
}
لماذا يشبه W3C ولكنه ليس المواصفة بالضبط؟
مواصفة مجموعة مجتمع رموز تصميم W3C لا تزال مسودة. تتبع figmascope الاتفاقيات الأساسية ($value و$type والمجموعات المتداخلة) لكنها لا تطبق جميع الحالات الهامشية كالأنواع المركبة وسلاسل حل الأسماء المستعارة. الناتج مستقر بما يكفي لاستهلاكه بواسطة Style Dictionary أو اجتياز JSON المباشر أو نموذج لغوي — وهو المستهلك الرئيسي.
أنواع الرموز المستخدمة
| $type | الفئة | تنسيق $value | مثال |
|---|---|---|---|
color |
اللون | سلسلة hex | "#d96a3a" |
dimension |
المسافات، نصف القطر | سلسلة CSS مع الوحدة | "16px" |
fontFamily |
الطباعة | سلسلة اسم الخط | "Inter" |
number |
الطباعة | رقم بلا وحدة | 400، 1.45 |
تُخرج الألوان دائمًا كسلاسل hex. إذا كان مصدر Figma يستخدم RGBA بتعتيم غير كامل، يُحفظ قناة الألفا في hex مكوّن من 8 أرقام (#d96a3a80).
كيف تحصل figmascope على الرموز
تستخدم figmascope استراتيجية مزدوجة المستوى. Figma Variables هي المصدر المفضل؛ والاستنتاج التلقائي هو الخيار الاحتياطي.
المستوى الأول: Figma Variables
إذا كان ملف Figma يحتوي على Variables محددة (نظام الرموز الأصلي في Figma، المتاح ضمن خطط Professional وOrganization)، تقرأها figmascope عبر نقطة نهاية REST API /v1/files/:key/variables/local. تُستخدم أسماء المتغيرات كمفاتيح للرموز، مع تحويلها إلى kebab-case. تُدمج الأوضاع في الوضع الافتراضي ما لم يكن الملف يحتوي على وضع واحد فقط، وعندئذ تُستخدم قيم ذلك الوضع مباشرة.
تُعيَّن مجموعات المتغيرات إلى مفاتيح الفئات العليا في tokens.json. مجموعة اسمها "Color" تُنتج tokens.color.*؛ و"Spacing" تُنتج tokens.spacing.*. إذا كان ملف Figma يستخدم أسماء مجموعات غير قياسية، تحاول figmascope استنتاج الفئة من نوع المتغير المُحلَّل.
المستوى الثاني: الاستنتاج من التكرار
كثير من ملفات Figma — خاصة القديمة منها أو الملفات من عملاء لم يتبنّوا Variables — لا تحتوي على تعريفات متغيرات. تتعامل figmascope مع هذا من خلال اجتياز شجرة العقد بأكملها وبناء رسوم بيانية لتكرار ألوان التعبئة وقيم المسافات وأنصاف أقطار الزوايا وخصائص الطباعة.
القيم التي تظهر فوق حد التكرار تصبح رموزًا مرشحة. تُسمَّى بفئتها ومؤشر تسلسلي (color.0، color.1...) ما لم يمكن استنتاج اسم مقروء من كيفية استخدام القيمة (مثلًا لون يُستخدم فقط على خلفيات عبر إطارات متعددة يصبح color.surface).
يسجّل بيان _meta.json حقل tokensSource إما "variables" أو "inferred"، لتتمكن من الإشارة في موجّه وكيلك إلى حالات الاستنتاج:
// _meta.json — الاستنتاج الاحتياطي
{
"tokensSource": "inferred",
"warnings": [
{
"code": "tokens-inferred",
"message": "No Figma Variables found. Tokens were inferred from usage frequency."
}
]
}
الرموز المستنتجة مفيدة لكنها ليست سلطة رسمية. تعامل معها كنقطة بداية لا كمواصفة نظام تصميم.
مراجع الرموز في IR التخطيط
تشير ملفات IR لكل شاشة (screens/*.json) إلى الرموز عبر مسار باستخدام سلاسل $ref بدلًا من تضمين القيم الخام. هذا يُبقي IR مضغوطًا ويضمن أن الوكيل يحل القيم دائمًا من مصدر حقيقة واحد:
{
"kind": "stack",
"name": "PrimaryButton",
"direction": "horizontal",
"gap": { "$ref": "spacing.2" },
"padding": {
"top": 10, "right": 16, "bottom": 10, "left": 16
},
"background": { "$ref": "color.accent" },
"radius": { "$ref": "radius.sm" },
"children": [
{
"kind": "leaf",
"name": "ButtonLabel",
"type": "text",
"style": { "$ref": "typography.body" },
"color": { "$ref": "color.surface" }
}
]
}
وكيل يعالج هذه العقدة يحل color.accent إلى #d96a3a وspacing.2 إلى 8px من ملف tokens.json الموازي. لا غموض، لا هلوسة في القيم.
اطلع على شرح IR لكل شاشة للاطلاع على توثيق مخطط العقدة الكامل.
استخدام tokens.json مع Style Dictionary
ناتج figmascope متوافق مع Style Dictionary مع أدنى قدر من الإعداد. بما أنه يستخدم بالفعل اتفاقية $value / $type، يمكنك توجيه Style Dictionary مباشرة إلى tokens.json:
// style-dictionary.config.js
module.exports = {
source: ['design/tokens.json'],
platforms: {
css: {
transformGroup: 'css',
prefix: 'fs',
buildPath: 'src/styles/',
files: [{ destination: 'tokens.css', format: 'css/variables' }]
},
js: {
transformGroup: 'js',
buildPath: 'src/',
files: [{ destination: 'tokens.js', format: 'javascript/es6' }]
}
}
};
تشغيل style-dictionary build سينتج خصائص CSS مخصصة وتصديرات وحدات ES من نفس الملف المصدر الذي يستخدمه الوكيل.
استخدام tokens.json مع Tailwind
سكريبت صغير يحوّل tokens.json إلى امتداد سمة Tailwind. الهيكل مسطح بما يكفي للاجتياز دون تكرار في الحالات الشائعة:
// scripts/tokens-to-tailwind.js
const fs = require('fs');
const tokens = JSON.parse(fs.readFileSync('design/tokens.json', 'utf8'));
function flattenGroup(group) {
return Object.fromEntries(
Object.entries(group).map(([k, v]) => [k, v.$value])
);
}
const extend = {
colors: flattenGroup(tokens.color),
spacing: flattenGroup(tokens.spacing),
borderRadius: flattenGroup(tokens.radius),
};
console.log(JSON.stringify(extend, null, 2));
node scripts/tokens-to-tailwind.js > design/tailwind-extend.json
ثم في tailwind.config.ts:
import extend from './design/tailwind-extend.json';
export default {
content: ['./src/**/*.{ts,tsx}'],
theme: { extend },
};
استخدام tokens.json مع Jetpack Compose
لمشاريع Android، يُعيَّن هيكل الرموز بشكل طبيعي إلى كائن Kotlin. يمكنك توليده برمجيًا أو طلب ذلك من Claude Code. لرموز الألوان:
// Generated from design/tokens.json
object DesignTokens {
object Color {
val surface = Color(0xFFF6F2EA)
val ink = Color(0xFF1F1D1A)
val accent = Color(0xFFD96A3A)
val accentSoft = Color(0xFFF2C7A8)
}
object Spacing {
val s1 = 4.dp
val s2 = 8.dp
val s4 = 16.dp
val s8 = 32.dp
}
}
اطلع على Jetpack Compose من Figma للحصول على دليل كامل يتضمن تكامل MaterialTheme.
ما لا يحتويه tokens.json
معرفة حدود النطاق تساعد على تحديد توقعات صحيحة:
- الظلال — لا تُصدّر figmascope حاليًا رموز drop-shadow أو inner-shadow. تظهر قيم الظل مضمّنة على العقد في IR حيثما وُجدت.
- التدرجات — تُصدَّر تعبئات التدرج كما هي على عقد الأوراق في IR، وليس كرموز مسماة.
- الحركة — قيم التوقيت والتخفيف والانتقال خارج النطاق. دعم Figma للحركة موجود في وضع Prototype الذي لا تقرؤه figmascope.
- نقاط التوقف — القيود التكيفية ليست مفهوم Figma على مستوى المتغيرات؛ فهي تتطلب قرارات تصميم منفصلة.
تشير مصفوفة warnings في _meta.json إلى أي قيم لم يمكن تصديرها بشكل نظيف. راجعها قبل تسليم الحزمة للوكيل.
كيف تُشتق أسماء الرموز
عندما تقرأ figmascope Figma Variables، يصبح اسم المتغير في Figma مفتاح الرمز. متغير اسمه Colors/Surface/Primary في مجموعة اسمها Color يصبح tokens.color.surface.primary في tokens.json — فاصل المسار هو / في Figma و. في مفاتيح JSON المتداخلة.
يسمح Figma بأسماء متغيرات تحتوي على مسافات وأحرف كبيرة وأحرف خاصة. تُوحّد figmascope هذه الأسماء:
- المسافات تصبح شرطات:
Primary Surface→primary-surface - الأحرف الكبيرة تصبح صغيرة:
AccentOrange→accent-orange - أحرف التحكم وعلامات bidi Unicode تُحذف عبر
sanitizeName - الكتابة السيريلية والنصوص غير اللاتينية الأخرى تُكتب صوتيًا لتوافق slug
خطوة الكتابة الصوتية مهمة للفرق الدولية. فريق تصميم أوكراني يستخدم أسماء متغيرات سيريلية مثل Фон (خلفية) يحصل على مفتاح ASCII ثابت (fon) في الناتج، يمكن استخدامه في أسماء كلاسات CSS وJSON دون مشاكل ترميز. يُحفظ الاسم الأصلي في حقل $description إن وُجد في بيانات تعريف Figma Variable.
التحقق من تغطية الرموز قبل توليد الكود
قبل تسليم الحزمة للوكيل، يستحق التحقق من أن تغطية الرموز تبدو معقولة. سكريبت Node سريع يتحقق من أن جميع قيم الألوان المرجوعة في IRs الشاشة تحل إلى مدخلات في tokens.json:
// scripts/check-token-coverage.js
const fs = require('fs');
const glob = require('glob');
const tokens = JSON.parse(fs.readFileSync('design/tokens.json', 'utf8'));
const screenFiles = glob.sync('design/screens/*.json');
function getRef(obj) {
const refs = [];
if (obj && typeof obj === 'object') {
if (obj.$ref) refs.push(obj.$ref);
for (const v of Object.values(obj)) refs.push(...getRef(v));
}
return refs;
}
function resolveRef(ref, tokens) {
return ref.split('.').reduce((o, k) => o?.[k], tokens);
}
let missing = 0;
for (const file of screenFiles) {
const screen = JSON.parse(fs.readFileSync(file, 'utf8'));
for (const ref of getRef(screen)) {
if (!resolveRef(ref, tokens)) {
console.error(`Missing token: ${ref} (in ${file})`);
missing++;
}
}
}
if (missing === 0) console.log('All token refs resolve.');
else console.error(`${missing} unresolved token refs.`);
صفر مراجع مفقودة يعني أن الوكيل سيتمكن من حل كل $ref في IR التخطيط دون اختراع قيم. إذا كانت هناك مراجع مفقودة، فهذا يعني عادةً أن ملف Figma تغيّر بعد تصدير الحزمة — أعد تشغيل figmascope للحصول على تصدير جديد. يمكنك أيضًا مراجعة مدونة figmascope للمزيد من النصائح حول الحفاظ على تغطية الرموز عبر تكرارات التصميم.