riazmo Claude Opus 4.6 commited on
Commit
56e8e7d
Β·
1 Parent(s): 13d9428

fix: typography overlap + semantic colors on separate rows

Browse files

Typography:
- Set textAutoResize='HEIGHT' and resize BEFORE setting characters
so Figma calculates actual wrapped text height
- Row height now uses actual sample.height instead of just fontSize
- Eliminates all text overlapping between typography rows

Colors β€” semantic categories (brand, text, bg, border, feedback):
- Sub-grouped by 2nd path segment (primary, secondary, error, info, etc.)
- Each sub-group gets its own row with label on the left
- e.g. brand: "primary" row with DEFAULT + 50/200/800/950 variants
feedback: "error" row, "info" row, "warning" row

Colors β€” palette hues (blue, green, neutral):
- Unchanged β€” horizontal grid layout for 50-900 ramp

Frames auto-resize to actual content height (no more estimates).
Copied to v2 directory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

output_json/figma-plugin-extracted/figma-design-token-creator 5/src/code.js CHANGED
@@ -915,14 +915,9 @@ figma.ui.onmessage = async function(msg) {
915
  for (var so = 0; so < semOrder.length; so++) { if (colorGroups[semOrder[so]]) sortedGroups.push(semOrder[so]); }
916
  for (var go = 0; go < groupOrder.length; go++) { if (sortedGroups.indexOf(groupOrder[go]) === -1) sortedGroups.push(groupOrder[go]); }
917
 
918
- // Calculate height: each group = heading(40) + one row of swatches(SWATCH_H_COLOR + SWATCH_META_H + 16)
919
- var colorsPerRow = Math.floor((CONTENT_W + SWATCH_GAP) / (SWATCH_W + SWATCH_GAP));
920
- var totalColorH = SECTION_PAD;
921
- for (var cgi = 0; cgi < sortedGroups.length; cgi++) {
922
- var rows = Math.ceil(colorGroups[sortedGroups[cgi]].length / colorsPerRow);
923
- totalColorH += 40 + rows * (SWATCH_H_COLOR + SWATCH_META_H + ITEM_GAP) + 16;
924
- }
925
- totalColorH += SECTION_PAD;
926
 
927
  var colorFrame = createSectionFrame('Colors', totalColorH);
928
  var cy = SECTION_PAD;
@@ -931,38 +926,22 @@ figma.ui.onmessage = async function(msg) {
931
  addText(colorFrame, 'COLORS', boldFont, 32, MARGIN, cy, DARK);
932
  cy += 48;
933
 
934
- for (var gi = 0; gi < sortedGroups.length; gi++) {
935
- var gName = sortedGroups[gi];
936
- var gColors = colorGroups[gName];
937
-
938
- // Group heading
939
- addText(colorFrame, gName.charAt(0).toUpperCase() + gName.slice(1), boldFont, 18, MARGIN, cy, DARK);
940
- cy += 36;
941
-
942
- for (var ci2 = 0; ci2 < gColors.length; ci2++) {
943
- var ct = gColors[ci2];
944
- var col = ci2 % colorsPerRow;
945
- var row = Math.floor(ci2 / colorsPerRow);
946
- var sx = MARGIN + col * (SWATCH_W + SWATCH_GAP);
947
- var sy = cy + row * (SWATCH_H_COLOR + SWATCH_META_H + ITEM_GAP);
948
-
949
- // Color fill rectangle (top part of card)
950
  var swatch = figma.createRectangle();
951
  swatch.resize(SWATCH_W, SWATCH_H_COLOR);
952
  swatch.x = sx; swatch.y = sy;
953
- var tl = 8, tr = 8, bl = 0, br = 0;
954
- swatch.topLeftRadius = tl; swatch.topRightRadius = tr;
955
- swatch.bottomLeftRadius = bl; swatch.bottomRightRadius = br;
956
  swatch.fills = [{ type: 'SOLID', color: hexToRgb(ct.value) }];
957
- colorFrame.appendChild(swatch);
958
 
959
- // AA badge on swatch
960
  var aa = getAAResult(ct.value);
961
- var badgeText = aa.passAA ? 'AA βœ“ ' + aa.ratio + ':1' : 'AA βœ— ' + aa.ratio + ':1';
962
  var badgeColor = aa.bestOn === 'white' ? { r: 1, g: 1, b: 1 } : { r: 0, g: 0, b: 0 };
963
- addText(colorFrame, badgeText, labelFont, 10, sx + 8, sy + SWATCH_H_COLOR - 18, badgeColor);
964
 
965
- // Metadata area (below swatch)
966
  var metaBg = figma.createRectangle();
967
  metaBg.resize(SWATCH_W, SWATCH_META_H);
968
  metaBg.x = sx; metaBg.y = sy + SWATCH_H_COLOR;
@@ -970,25 +949,78 @@ figma.ui.onmessage = async function(msg) {
970
  metaBg.fills = [{ type: 'SOLID', color: { r: 0.97, g: 0.97, b: 0.98 } }];
971
  metaBg.strokes = [{ type: 'SOLID', color: { r: 0.9, g: 0.9, b: 0.9 } }];
972
  metaBg.strokeWeight = 1;
973
- colorFrame.appendChild(metaBg);
974
 
975
- // Token name
976
  var nameParts = ct.name.split('/');
977
  var displayName = nameParts.filter(function(p) { return p !== 'DEFAULT'; }).join('/');
978
- addText(colorFrame, displayName, boldFont, 11, sx + 10, sy + SWATCH_H_COLOR + 8, DARK);
 
979
 
980
- // Hex value
981
- addText(colorFrame, ct.value.toUpperCase(), labelFont, 11, sx + 10, sy + SWATCH_H_COLOR + 26, MUTED);
982
-
983
- // Contrast info
984
  var contrastText = aa.passAA ? 'AA Pass on ' + aa.bestOn : 'AA Fail (' + aa.ratio + ':1)';
985
  var contrastColor = aa.passAA ? { r: 0.13, g: 0.55, b: 0.13 } : { r: 0.85, g: 0.18, b: 0.18 };
986
- addText(colorFrame, contrastText, labelFont, 10, sx + 10, sy + SWATCH_H_COLOR + 44, contrastColor);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
987
  }
988
 
989
- var gRows = Math.ceil(gColors.length / colorsPerRow);
990
- cy += gRows * (SWATCH_H_COLOR + SWATCH_META_H + ITEM_GAP) + 16;
991
  }
 
 
 
992
  }
993
 
994
  // ══════════════════════════════════════════════════════════
@@ -1017,13 +1049,8 @@ figma.ui.onmessage = async function(msg) {
1017
  function renderTypoFrame(typoList, frameName) {
1018
  if (typoList.length === 0) return;
1019
 
1020
- // Estimate height: each row ~ 80-120px depending on font size
1021
- var estH = SECTION_PAD + 60;
1022
- for (var i = 0; i < typoList.length; i++) {
1023
- var fs = parseNumericValue(typoList[i].value.fontSize) || 16;
1024
- estH += Math.max(Math.min(fs, 56), 24) + 64; // sample + labels + gap
1025
- }
1026
- estH += SECTION_PAD;
1027
 
1028
  var frame = createSectionFrame(frameName, estH);
1029
  var fy = SECTION_PAD;
@@ -1062,17 +1089,21 @@ figma.ui.onmessage = async function(msg) {
1062
  var tierName = tierParts.filter(function(p) { return p !== 'desktop' && p !== 'mobile'; }).join('.');
1063
  addText(frame, tierName, boldFont, 13, COL_NAME, fy + 4, DARK);
1064
 
1065
- // Sample text in actual font
1066
  var useBold = (tierName.indexOf('display') > -1 || tierName.indexOf('heading') > -1);
1067
  var sample = figma.createText();
1068
  sample.fontName = useBold ? sampleFontBold : sampleFont;
1069
  sample.fontSize = displaySize;
 
 
 
1070
  sample.characters = getSampleText(tt.name);
1071
  sample.x = COL_SAMPLE; sample.y = fy;
1072
- sample.resize(540, sample.height);
1073
- sample.textAutoResize = 'HEIGHT';
1074
  frame.appendChild(sample);
1075
 
 
 
 
1076
  // Specs column β€” stacked chips
1077
  var specY = fy;
1078
  addText(frame, 'Size: ' + fSize + 'px', boldFont, 12, COL_SPECS, specY, DARK);
@@ -1083,7 +1114,9 @@ figma.ui.onmessage = async function(msg) {
1083
  specY += 18;
1084
  addText(frame, 'Font: ' + fFamily, labelFont, 12, COL_SPECS, specY, BLUE);
1085
 
1086
- var rowH = Math.max(displaySize + 8, specY - fy + 24);
 
 
1087
  fy += rowH + 8;
1088
 
1089
  // Row separator
 
915
  for (var so = 0; so < semOrder.length; so++) { if (colorGroups[semOrder[so]]) sortedGroups.push(semOrder[so]); }
916
  for (var go = 0; go < groupOrder.length; go++) { if (sortedGroups.indexOf(groupOrder[go]) === -1) sortedGroups.push(groupOrder[go]); }
917
 
918
+ // Calculate initial height estimate (will be resized at end)
919
+ var colorsPerRow = Math.floor((CONTENT_W - 140 + SWATCH_GAP) / (SWATCH_W + SWATCH_GAP));
920
+ var totalColorH = 2000; // generous initial estimate, resized later
 
 
 
 
 
921
 
922
  var colorFrame = createSectionFrame('Colors', totalColorH);
923
  var cy = SECTION_PAD;
 
926
  addText(colorFrame, 'COLORS', boldFont, 32, MARGIN, cy, DARK);
927
  cy += 48;
928
 
929
+ // Helper to render a single color card at position
930
+ function renderColorCard(frame, ct, sx, sy) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
931
  var swatch = figma.createRectangle();
932
  swatch.resize(SWATCH_W, SWATCH_H_COLOR);
933
  swatch.x = sx; swatch.y = sy;
934
+ swatch.topLeftRadius = 8; swatch.topRightRadius = 8;
 
 
935
  swatch.fills = [{ type: 'SOLID', color: hexToRgb(ct.value) }];
936
+ frame.appendChild(swatch);
937
 
938
+ // AA badge
939
  var aa = getAAResult(ct.value);
940
+ var badgeText = aa.passAA ? 'AA βœ“ ' + aa.ratio + ':1' : 'AA βœ— ' + aa.ratio + ':1';
941
  var badgeColor = aa.bestOn === 'white' ? { r: 1, g: 1, b: 1 } : { r: 0, g: 0, b: 0 };
942
+ addText(frame, badgeText, labelFont, 10, sx + 8, sy + SWATCH_H_COLOR - 18, badgeColor);
943
 
944
+ // Metadata area
945
  var metaBg = figma.createRectangle();
946
  metaBg.resize(SWATCH_W, SWATCH_META_H);
947
  metaBg.x = sx; metaBg.y = sy + SWATCH_H_COLOR;
 
949
  metaBg.fills = [{ type: 'SOLID', color: { r: 0.97, g: 0.97, b: 0.98 } }];
950
  metaBg.strokes = [{ type: 'SOLID', color: { r: 0.9, g: 0.9, b: 0.9 } }];
951
  metaBg.strokeWeight = 1;
952
+ frame.appendChild(metaBg);
953
 
 
954
  var nameParts = ct.name.split('/');
955
  var displayName = nameParts.filter(function(p) { return p !== 'DEFAULT'; }).join('/');
956
+ addText(frame, displayName, boldFont, 11, sx + 10, sy + SWATCH_H_COLOR + 8, DARK);
957
+ addText(frame, ct.value.toUpperCase(), labelFont, 11, sx + 10, sy + SWATCH_H_COLOR + 26, MUTED);
958
 
 
 
 
 
959
  var contrastText = aa.passAA ? 'AA Pass on ' + aa.bestOn : 'AA Fail (' + aa.ratio + ':1)';
960
  var contrastColor = aa.passAA ? { r: 0.13, g: 0.55, b: 0.13 } : { r: 0.85, g: 0.18, b: 0.18 };
961
+ addText(frame, contrastText, labelFont, 10, sx + 10, sy + SWATCH_H_COLOR + 44, contrastColor);
962
+ }
963
+
964
+ var isSemantic = { 'brand': 1, 'text': 1, 'bg': 1, 'background': 1, 'border': 1, 'feedback': 1 };
965
+ var CARD_H = SWATCH_H_COLOR + SWATCH_META_H + ITEM_GAP;
966
+
967
+ for (var gi = 0; gi < sortedGroups.length; gi++) {
968
+ var gName = sortedGroups[gi];
969
+ var gColors = colorGroups[gName];
970
+
971
+ // Group heading
972
+ addText(colorFrame, gName.charAt(0).toUpperCase() + gName.slice(1), boldFont, 18, MARGIN, cy, DARK);
973
+ cy += 36;
974
+
975
+ if (isSemantic[gName]) {
976
+ // ── SEMANTIC LAYOUT: sub-group by 2nd path segment, each on own row ──
977
+ // e.g. brand/primary/DEFAULT, brand/primary/50 β†’ sub-group "primary"
978
+ // feedback/error/DEFAULT, feedback/info/DEFAULT β†’ sub-groups "error", "info"
979
+ var subGroups = {};
980
+ var subOrder = [];
981
+ for (var si = 0; si < gColors.length; si++) {
982
+ var sp = gColors[si].name.split('/');
983
+ var subKey = sp.length > 1 ? sp[1] : sp[0];
984
+ if (!subGroups[subKey]) { subGroups[subKey] = []; subOrder.push(subKey); }
985
+ subGroups[subKey].push(gColors[si]);
986
+ }
987
+
988
+ for (var ski = 0; ski < subOrder.length; ski++) {
989
+ var subName = subOrder[ski];
990
+ var subColors = subGroups[subName];
991
+
992
+ // Row label on the left
993
+ addText(colorFrame, subName, boldFont, 13, MARGIN, cy + 30, MUTED);
994
+
995
+ // Swatches start after label
996
+ var labelW = 140;
997
+ for (var sci = 0; sci < subColors.length; sci++) {
998
+ var scx = MARGIN + labelW + sci * (SWATCH_W + SWATCH_GAP);
999
+ renderColorCard(colorFrame, subColors[sci], scx, cy);
1000
+ }
1001
+
1002
+ cy += CARD_H + 8;
1003
+ }
1004
+ } else {
1005
+ // ── PALETTE LAYOUT: horizontal grid (50-900 ramp) ──
1006
+ for (var ci2 = 0; ci2 < gColors.length; ci2++) {
1007
+ var ct = gColors[ci2];
1008
+ var col = ci2 % colorsPerRow;
1009
+ var row = Math.floor(ci2 / colorsPerRow);
1010
+ var sx = MARGIN + col * (SWATCH_W + SWATCH_GAP);
1011
+ var sy = cy + row * CARD_H;
1012
+ renderColorCard(colorFrame, ct, sx, sy);
1013
+ }
1014
+
1015
+ var gRows = Math.ceil(gColors.length / colorsPerRow);
1016
+ cy += gRows * CARD_H + 16;
1017
  }
1018
 
1019
+ cy += 8; // gap between groups
 
1020
  }
1021
+
1022
+ // Resize color frame to actual content
1023
+ colorFrame.resize(FRAME_W, cy + SECTION_PAD);
1024
  }
1025
 
1026
  // ══════════════════════════════════════════════════════════
 
1049
  function renderTypoFrame(typoList, frameName) {
1050
  if (typoList.length === 0) return;
1051
 
1052
+ // Use generous initial height β€” will be resized to actual content
1053
+ var estH = 3000;
 
 
 
 
 
1054
 
1055
  var frame = createSectionFrame(frameName, estH);
1056
  var fy = SECTION_PAD;
 
1089
  var tierName = tierParts.filter(function(p) { return p !== 'desktop' && p !== 'mobile'; }).join('.');
1090
  addText(frame, tierName, boldFont, 13, COL_NAME, fy + 4, DARK);
1091
 
1092
+ // Sample text in actual font β€” use fixed width so it wraps, then measure height
1093
  var useBold = (tierName.indexOf('display') > -1 || tierName.indexOf('heading') > -1);
1094
  var sample = figma.createText();
1095
  sample.fontName = useBold ? sampleFontBold : sampleFont;
1096
  sample.fontSize = displaySize;
1097
+ // Set fixed width BEFORE setting characters so text wraps properly
1098
+ sample.textAutoResize = 'HEIGHT';
1099
+ sample.resize(540, displaySize * 2);
1100
  sample.characters = getSampleText(tt.name);
1101
  sample.x = COL_SAMPLE; sample.y = fy;
 
 
1102
  frame.appendChild(sample);
1103
 
1104
+ // Actual sample height after text rendering
1105
+ var sampleH = sample.height;
1106
+
1107
  // Specs column β€” stacked chips
1108
  var specY = fy;
1109
  addText(frame, 'Size: ' + fSize + 'px', boldFont, 12, COL_SPECS, specY, DARK);
 
1114
  specY += 18;
1115
  addText(frame, 'Font: ' + fFamily, labelFont, 12, COL_SPECS, specY, BLUE);
1116
 
1117
+ // Row height = max of (sample text height, spec labels height, minimum 40px)
1118
+ var specsH = specY - fy + 24;
1119
+ var rowH = Math.max(sampleH + 12, specsH, 40);
1120
  fy += rowH + 8;
1121
 
1122
  // Row separator