-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit f1c8d30
Showing
6 changed files
with
945 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Fix San Francisco | ||
|
||
**Warning:** This plugin won't fix the city's problems, but it will automatically apply the [correct font variant and tracking](https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/typography/#font-usage-and-tracking) for selected texts using the San Francisco typeface. | ||
|
||
Usually, you'd use the built-in text styles when designing for iOS. However, if your project needs to use custom font sizes, this plugin's got your back for a more accurate representation of how they're rendered in iOS. | ||
|
||
Inspired by [Sketch-SF-UI-Font-Fixer](https://github.com/kylehickinson/Sketch-SF-UI-Font-Fixer). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
/** | ||
* Fix San Francisco | ||
* Fixes texts with SF typeface according to Apple's official tracking table: | ||
* https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/typography/#font-usage-and-tracking | ||
*/ | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
const FONT_DISPLAY = "SF Pro Display"; | ||
const FONT_TEXT = "SF Pro Text"; | ||
const SIZE_MIN = 6; | ||
const SIZE_MAX = 79; | ||
const SIZE_SWAP = 20; | ||
const TRACKING_UNIT = 1000; | ||
// Fills missing font size values inbetween defined values | ||
function fillTracking(arr, min, max) { | ||
let value = 0; | ||
for (let i = min; i < max; i++) { | ||
if (i in arr) { | ||
value = arr[i]; | ||
continue; | ||
} | ||
arr[i] = value; | ||
} | ||
return arr; | ||
} | ||
const TRACKING_DISPLAY = fillTracking({ | ||
20: 19, | ||
21: 17, | ||
22: 16, | ||
24: 15, | ||
25: 14, | ||
27: 13, | ||
30: 12, | ||
33: 11, | ||
40: 10, | ||
44: 9, | ||
48: 8, | ||
50: 7, | ||
53: 6, | ||
56: 5, | ||
60: 4, | ||
65: 3, | ||
69: 2 | ||
}, SIZE_SWAP, SIZE_MAX); | ||
const TRACKING_TEXT = fillTracking({ | ||
6: 41, | ||
8: 26, | ||
9: 19, | ||
10: 12, | ||
11: 6, | ||
12: 0, | ||
13: -6, | ||
14: -11, | ||
15: -16, | ||
16: -20, | ||
17: -24, | ||
18: -25, | ||
19: -26 | ||
}, SIZE_MIN, SIZE_SWAP); | ||
function traverse() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const nodes = figma.currentPage.selection; | ||
let modifiedCount = 0; | ||
for (let i = 0; i < nodes.length; i++) { | ||
const node = nodes[i]; | ||
if (node.type !== "TEXT") | ||
continue; | ||
let isModified = false; | ||
let fontFamily = node.fontName.family; | ||
const fontSize = node.fontSize; | ||
const letterSpacing = node.letterSpacing; | ||
if (fontFamily !== FONT_DISPLAY && fontFamily !== FONT_TEXT) | ||
continue; | ||
if (fontFamily === FONT_DISPLAY && fontSize < SIZE_SWAP) { | ||
fontFamily = FONT_TEXT; | ||
isModified = true; | ||
} | ||
if (fontFamily === FONT_TEXT && fontSize >= SIZE_SWAP) { | ||
fontFamily = FONT_DISPLAY; | ||
isModified = true; | ||
} | ||
// Load and assign font family | ||
const fontName = { | ||
family: fontFamily, | ||
style: node.fontName.style | ||
}; | ||
yield figma.loadFontAsync(fontName); | ||
node.fontName = fontName; | ||
// Set tracking | ||
switch (fontFamily) { | ||
case FONT_DISPLAY: | ||
if (fontSize >= SIZE_MAX) { | ||
node.letterSpacing = { | ||
value: 0, | ||
unit: "PIXELS" | ||
}; | ||
break; | ||
} | ||
node.letterSpacing = { | ||
value: (fontSize * TRACKING_DISPLAY[fontSize]) / TRACKING_UNIT, | ||
unit: "PIXELS" | ||
}; | ||
break; | ||
case FONT_TEXT: | ||
node.letterSpacing = { | ||
value: (fontSize * TRACKING_TEXT[Math.max(SIZE_MIN, fontSize)]) / | ||
TRACKING_UNIT, | ||
unit: "PIXELS" | ||
}; | ||
break; | ||
} | ||
// Check if text node is modified | ||
if (JSON.stringify(node.letterSpacing) !== JSON.stringify(letterSpacing)) | ||
isModified = true; | ||
if (isModified) | ||
modifiedCount++; | ||
} | ||
figma.closePlugin(modifiedCount | ||
? `Updated ${modifiedCount} texts with SF typeface.` | ||
: "No texts were updated."); | ||
}); | ||
} | ||
traverse(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
/** | ||
* Fix San Francisco | ||
* Fixes texts with SF typeface according to Apple's official tracking table: | ||
* https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/typography/#font-usage-and-tracking | ||
*/ | ||
|
||
const FONT_DISPLAY = "SF Pro Display" | ||
const FONT_TEXT = "SF Pro Text" | ||
|
||
const SIZE_MIN = 6 | ||
const SIZE_MAX = 79 | ||
const SIZE_SWAP = 20 | ||
const TRACKING_UNIT = 1000 | ||
|
||
// Fills missing font size values inbetween defined values | ||
function fillTracking(arr, min, max) { | ||
let value = 0 | ||
for (let i = min; i < max; i++) { | ||
if (i in arr) { | ||
value = arr[i] | ||
continue | ||
} | ||
arr[i] = value | ||
} | ||
return arr | ||
} | ||
|
||
const TRACKING_DISPLAY = fillTracking( | ||
{ | ||
20: 19, | ||
21: 17, | ||
22: 16, | ||
24: 15, | ||
25: 14, | ||
27: 13, | ||
30: 12, | ||
33: 11, | ||
40: 10, | ||
44: 9, | ||
48: 8, | ||
50: 7, | ||
53: 6, | ||
56: 5, | ||
60: 4, | ||
65: 3, | ||
69: 2 | ||
}, | ||
SIZE_SWAP, | ||
SIZE_MAX | ||
) | ||
|
||
const TRACKING_TEXT = fillTracking( | ||
{ | ||
6: 41, | ||
8: 26, | ||
9: 19, | ||
10: 12, | ||
11: 6, | ||
12: 0, | ||
13: -6, | ||
14: -11, | ||
15: -16, | ||
16: -20, | ||
17: -24, | ||
18: -25, | ||
19: -26 | ||
}, | ||
SIZE_MIN, | ||
SIZE_SWAP | ||
) | ||
|
||
async function traverse() { | ||
const nodes = figma.currentPage.selection | ||
let modifiedCount = 0 | ||
|
||
for (let i = 0; i < nodes.length; i++) { | ||
const node = nodes[i] | ||
if (node.type !== "TEXT") continue | ||
|
||
let isModified = false | ||
let fontFamily = (node.fontName as FontName).family | ||
const fontSize = node.fontSize as number | ||
const letterSpacing = node.letterSpacing | ||
|
||
if (fontFamily !== FONT_DISPLAY && fontFamily !== FONT_TEXT) continue | ||
|
||
if (fontFamily === FONT_DISPLAY && fontSize < SIZE_SWAP) { | ||
fontFamily = FONT_TEXT | ||
isModified = true | ||
} | ||
|
||
if (fontFamily === FONT_TEXT && fontSize >= SIZE_SWAP) { | ||
fontFamily = FONT_DISPLAY | ||
isModified = true | ||
} | ||
|
||
// Load and assign font family | ||
const fontName = { | ||
family: fontFamily, | ||
style: (node.fontName as FontName).style | ||
} | ||
await figma.loadFontAsync(fontName) | ||
node.fontName = fontName | ||
|
||
// Set tracking | ||
switch (fontFamily) { | ||
case FONT_DISPLAY: | ||
if (fontSize >= SIZE_MAX) { | ||
node.letterSpacing = { | ||
value: 0, | ||
unit: "PIXELS" | ||
} | ||
break | ||
} | ||
node.letterSpacing = { | ||
value: (fontSize * TRACKING_DISPLAY[fontSize]) / TRACKING_UNIT, | ||
unit: "PIXELS" | ||
} | ||
break | ||
case FONT_TEXT: | ||
node.letterSpacing = { | ||
value: | ||
(fontSize * TRACKING_TEXT[Math.max(SIZE_MIN, fontSize)]) / | ||
TRACKING_UNIT, | ||
unit: "PIXELS" | ||
} | ||
break | ||
} | ||
|
||
// Check if text node is modified | ||
if (JSON.stringify(node.letterSpacing) !== JSON.stringify(letterSpacing)) | ||
isModified = true | ||
if (isModified) modifiedCount++ | ||
} | ||
|
||
figma.closePlugin( | ||
modifiedCount | ||
? `Updated ${modifiedCount} texts with SF typeface.` | ||
: "No texts were updated." | ||
) | ||
} | ||
|
||
traverse() |
Oops, something went wrong.