I am working on a font picker plugin and I ran into a similar problem, so I started investigating the google fonts main distribution site until I found what I was looking for.
Google's fonts site executes a call to the following API endpoint.
https://fonts.google.com/metadata/fonts
Which returns the following text file.
)]}'{"axisRegistry": [
{
"tag": "FILL",
"displayName": "Fill",
"min": 0.0,
"defaultValue": 0.0,
"max": 1.0,
"precision": -2,
"description": "The Fill axis is intended to provide a treatment of the design that fills in transparent forms with opaque ones (and sometimes interior opaque forms become transparent, to maintain contrasting shapes). Transitions often occur from the center, a side, or a corner, but can be in any direction. This can be useful in animation or interaction to convey a state transition. The numbers indicate proportion filled, from 0 (no treatment) to 1 (completely filled).",
"fallbacks": [
{
"name": "Normal",
"value": 0.0
},
{
"name": "Filled",
"value": 1.0
}
]
} ...],"familyMetadataList": [{
"family": "Alegreya",
"displayName": null,
"category": "Serif",
"size": 425570,
"subsets": [
"menu",
"cyrillic",
"cyrillic-ext",
"greek",
"greek-ext",
"latin",
"latin-ext",
"vietnamese"
],
"fonts": {
"400": {
"thickness": 4,
"slant": 1,
"width": 6,
"lineHeight": 1.361
},
"400i": {
"thickness": 4,
"slant": 4,
"width": 6,
"lineHeight": 1.361
},
"500": {
"thickness": null,
"slant": null,
"width": null,
"lineHeight": 1.361
},
"500i": {
"thickness": null,
"slant": null,
"width": null,
"lineHeight": 1.361
},
"600": {
"thickness": null,
"slant": null,
"width": null,
"lineHeight": 1.361
},
"600i": {
"thickness": null,
"slant": null,
"width": null,
"lineHeight": 1.361
},
"700": {
"thickness": 7,
"slant": 1,
"width": 7,
"lineHeight": 1.361
},
"700i": {
"thickness": 6,
"slant": 4,
"width": 6,
"lineHeight": 1.361
},
"800": {
"thickness": null,
"slant": null,
"width": null,
"lineHeight": 1.361
},
"800i": {
"thickness": null,
"slant": null,
"width": null,
"lineHeight": 1.361
},
"900": {
"thickness": 8,
"slant": 1,
"width": 7,
"lineHeight": 1.361
},
"900i": {
"thickness": 8,
"slant": 4,
"width": 6,
"lineHeight": 1.361
}
},
"axes": [
{
"tag": "wght",
"min": 400.0,
"max": 900.0,
"defaultValue": 400.0
}
],
"unsupportedAxes": [],
"designers": [
"Juan Pablo del Peral",
"Huerta Tipográfica"
],
"lastModified": "2021-02-11",
"dateAdded": "2011-12-19",
"popularity": 159,
"trending": 828,
"defaultSort": 164,
"androidFragment": null,
"isNoto": false
}...],...}
Please note that while the above looks like a JSON file, it will be treated as a text file, so you will have to remove this part )]}'
from the top of the text file, so you can then parse it as a JSON file.
The only top-level property that matters (as far as your use case is concerned) is the "familyMetadataList" property, as the name implies it includes all the fonts metadata, which includes the axes any given font has.
You will have to loop on the "familyMetadataList" prop and see if the font's axes member has an array that is not empty, from there we can deduce that it is a variable font.
You can do something as simple as this to figure out which font is variable.
const variableFonts=[];
const googleFontJSON = {
"familyMetadataList": [
{
"family": "Alegreya",
"displayName": null,
"category": "Serif",
"size": 425570,
"subsets": [
"menu",
"cyrillic",
"cyrillic-ext",
"greek",
"greek-ext",
"latin",
"latin-ext",
"vietnamese"
],
"fonts": {
"400": {
"thickness": 4,
"slant": 1,
"width": 6,
"lineHeight": 1.361
},
"400i": {
"thickness": 4,
"slant": 4,
"width": 6,
"lineHeight": 1.361
},
"500": {
"thickness": null,
"slant": null,
"width": null,
"lineHeight": 1.361
},
"500i": {
"thickness": null,
"slant": null,
"width": null,
"lineHeight": 1.361
},
"600": {
"thickness": null,
"slant": null,
"width": null,
"lineHeight": 1.361
},
"600i": {
"thickness": null,
"slant": null,
"width": null,
"lineHeight": 1.361
},
"700": {
"thickness": 7,
"slant": 1,
"width": 7,
"lineHeight": 1.361
},
"700i": {
"thickness": 6,
"slant": 4,
"width": 6,
"lineHeight": 1.361
},
"800": {
"thickness": null,
"slant": null,
"width": null,
"lineHeight": 1.361
},
"800i": {
"thickness": null,
"slant": null,
"width": null,
"lineHeight": 1.361
},
"900": {
"thickness": 8,
"slant": 1,
"width": 7,
"lineHeight": 1.361
},
"900i": {
"thickness": 8,
"slant": 4,
"width": 6,
"lineHeight": 1.361
}
},
"axes": [
{
"tag": "wght",
"min": 400.0,
"max": 900.0,
"defaultValue": 400.0
}
],
"unsupportedAxes": [],
"designers": [
"Juan Pablo del Peral",
"Huerta Tipográfica"
],
"lastModified": "2021-02-11",
"dateAdded": "2011-12-19",
"popularity": 159,
"trending": 828,
"defaultSort": 164,
"androidFragment": null,
"isNoto": false
},
{
"family": "Alegreya SC",
"displayName": null,
"category": "Serif",
"size": 381295,
"subsets": [
"menu",
"cyrillic",
"cyrillic-ext",
"greek",
"greek-ext",
"latin",
"latin-ext",
"vietnamese"
],
"fonts": {
"400": {
"thickness": 4,
"slant": 1,
"width": 7,
"lineHeight": 1.361
},
"400i": {
"thickness": 4,
"slant": 4,
"width": 7,
"lineHeight": 1.361
},
"500": {
"thickness": null,
"slant": null,
"width": null,
"lineHeight": 1.361
},
"500i": {
"thickness": null,
"slant": null,
"width": null,
"lineHeight": 1.361
},
"700": {
"thickness": 6,
"slant": 1,
"width": 7,
"lineHeight": 1.361
},
"700i": {
"thickness": 6,
"slant": 3,
"width": 7,
"lineHeight": 1.361
},
"800": {
"thickness": null,
"slant": null,
"width": null,
"lineHeight": 1.361
},
"800i": {
"thickness": null,
"slant": null,
"width": null,
"lineHeight": 1.361
},
"900": {
"thickness": 8,
"slant": 1,
"width": 7,
"lineHeight": 1.361
},
"900i": {
"thickness": 8,
"slant": 3,
"width": 7,
"lineHeight": 1.361
}
},
"axes": [],
"unsupportedAxes": [],
"designers": [
"Juan Pablo del Peral",
"Huerta Tipográfica"
],
"lastModified": "2021-03-24",
"dateAdded": "2011-12-19",
"popularity": 436,
"trending": 249,
"defaultSort": 443,
"androidFragment": null,
"isNoto": false
}
]}; // The array of font meta data
googleFontJSON.familyMetadataList.forEach(font => {
if (font.axes.length) {
font.isVariable=true;
} else {
font.isVariable=false;
}
});
console.log(googleFontJSON);
How you analyze the data is of course entirely your own prerogative.
Good luck with your project, Mr.Steven.
You can also acquire more information about any given variable font's axes step through the axis registry prop found JSON file found at https://fonts.google.com/metadata/fonts.
Simply examine the precision prop.
For example, axes with a 0.1 step like "opsz" and "wdth" have their precision set to -1, axes with a 0.01 step like "CASL" and "MONO" have their precision set to -2.
"axisRegistry": [
{
"tag": "opsz",
"displayName": "Optical size",
"min": 6.0,
"defaultValue": 14.0,
"max": 144.0,
"precision": -1, //<=== Here
"description": "Adapt the ...",
"fallbacks": [
{
"name": "6pt",
"value": 6.0
},
{
"name": "7pt",
"value": 7.0
}...
]
},...