The stylistic axis "grade" refers to changes of font outlines to make type more or less dense, specifically without causing any change to the width of letters. As Mozilla defines it:
The term 'grade' refers to the relative weight or density of the typeface design, but differs from traditional 'weight' in that the physical space the text occupies does not change, so changing the text grade doesn't change the overall layout of the text or elements around it.
I wanted to test out the grades of Signika vs Signika Negative, in their static versions currently-hosted on Google Fonts. These are intended as two grades of the same typeface, but do they actually meet the no-text-reflow criterion of "grades," or are they simply different font weights?
As this test shows, they cause reflow:
This shows that the two versions are, in fact, different weights rather than different grades. Or, at least, they might be called "grades" by some people, but they change letters on both stroke thickness and in character width.
This is consistent with the Glyphs source. It has two masters:
...and the postive/negative instances are derived from that (here, the Signika Negative Light is an extrapolated -15 weight):
I'll add the shifted instances as new masters for grade, then write a script to duplicate letter widths from the normal to these Negative masters. I'll try keeping the ratio of spacing the same between left and right sides, to prevent spacing from being worsened by the update.
This will allow a grade axis (GRAD) in the future.
As the previous doc says, a Grade axis should keep widths and prevent reflow. According to Mozilla Dev Network:
The term 'grade' refers to the relative weight or density of the typeface design, but differs from traditional 'weight' in that the physical space the text occupies does not change, so changing the text grade doesn't change the overall layout of the text or elements around it.
Signika and Signika Negative have a problem: they don't currently share text width, even though they should. This prevents grade from being used in a truly useful way on the web, where the primary function is likely to be making light/dark UI changes a possibility.
Negative Lightwght value of -15, and converting it to a masterLight Negative Light
At first, the VF that is generating is not including the Grade axis. To fix this, I'm trying a number of things:

| Master | Weight | Grade |
|---|---|---|
| Light-GRAD | 0 | 0 |
| Light | 0 | 100 |
| Bold | 1000 | 100 |
| Black | 1350 | 100 |
0or 100, depending on their normal/negative nameStrangely, so far this is leading FontMake to export a designspace with GRAD
<axis default="100" maximum="100" minimum="100" name="Grade" tag="GRAD" />
.designspace<axes> <axis default="300" maximum="900" minimum="300" name="Weight" tag="wght"> <map input="300" output="0" /> <map input="400" output="291" /> <map input="600" output="577" /> <map input="700" output="843" /> <map input="900" output="1350" /> </axis> <axis default="100" maximum="100" minimum="0" name="Grade" tag="GRAD" /> </axes>
Changing:
wght default 300input="900"GRAD 0.designspace without instances, because these have wght values outside of the minand max -15).It builds! There are some issues.
Most pressing: the grade axis is so subtle, it barely changes the points in the VF. It's hard to understand why immediately. Is this because...
Ahhh the Light static instance has a wght value of 50, while the Negative Light has a wghtof -15. Meanwhile, the VF has a min of 0, so the Grade axis may not have as far it can travel
Lightwght Lightbigger difference between Grade
much
axes.
I've extrapolated a very light Grade instance (-300 weight), then made this a master and copied the Light metrics.
I was having issues exporting from FontMake...
KeyError: 'wght'
AssertionError: ((236, [227, 236, 236, 236]), 'int', '.BaseCount', 'BaseArray', '.BaseArray', 'MarkBasePos', '[0]', 'Lookup', '[2]', 'list', '.Lookup', 'LookupList', '.LookupList', 'GPOS', '.table', 'table_G_P_O_S_')
...so I'm instead trying to export with GlyphsApp's built-in exporter. This helpfully tells me which glyphs are blocking the export, due to "too many inflection points"
The plugin RedArrows is extremely helpful in locating inflection points and other issues.
I also got an error **The font contains glyphs with duplicate production names:** for commaaccentcomb and commaaccent. I disabled commaaccentcomb for now, to get past this.
Unfortunately, Glyphs export seems to be duplicating the Light GRAD master to make a faulty multiple-master VF, where the Bold Negative corner duplicates the Light Negative corner.
a, G, @, and resolved commaaccentcomb and commaaccent.I've set Negative Instances to have weight values matching the positive instances.
However, I'm getting this error from FontMake:
AssertionError: Location for axis 'Weight' (mapped to 250.0) out of range for 'Signika Light-GRAD' [300.0..700.0]
Probably due to the light masters having a value of 0, while the light instances have a weight value of 50.
It appears that it may not be possible to export a 2-axis variable font with just 3 masters. Here's a relevant GitHub Issue: https://github.com/googlei18n/fontmake/issues/454#issuecomment-431494121
Because it seems that a 3-master, 2-axis VF may not currently be possible to export, I make this with 4-masters, simply as a proof of concept. The instance values, kerning, and metadata will still be wrong, but it should work at a basic state.
I learned on this FontMake Issue
I had the grad-min set as the default, rather than the grad-max. The GRAD default must match the Grade of the main masters on the weight axis.
It's now exporting and is 175kb for the TTF, versus the 206kb that 4 masters produced.
This seems to be working well:
<axes>
<axis default="300" maximum="750" minimum="300" name="Weight" tag="wght">
# (I'll be tweaking these map values more, so they're not final)
<map input="300" output="0" />
<map input="400" output="360" />
<map input="600" output="650" />
<map input="700" output="920" />
<map input="750" output="1000" />
</axis>
#################
# this is the critical bit – the default *must* match the GRAD of the two main masters for wght, not the GRAD in the min-grade master
<axis default="100" maximum="100" minimum="0" name="Grade" tag="GRAD" />
#################
</axes>
# here are my 3 sources
<sources>
<source familyname="Signika" filename="Signika-Light-GRAD.ufo" name="Signika Light-GRAD" stylename="Light-GRAD">
<lib copy="1" />
<groups copy="1" />
<features copy="1" />
<info copy="1" />
<location>
<dimension name="Weight" xvalue="0" />
<dimension name="Grade" xvalue="0" />
</location>
</source>
<source familyname="Signika" filename="Signika-Light.ufo" name="Signika Light" stylename="Light">
<location>
<dimension name="Weight" xvalue="0" />
<dimension name="Grade" xvalue="100" />
</location>
</source>
<source familyname="Signika" filename="Signika-Bold.ufo" name="Signika Bold" stylename="Bold">
<location>
<dimension name="Weight" xvalue="1000" />
<dimension name="Grade" xvalue="100" />
</location>
</source>
</sources>
I'll delete the "Bold GRAD" master from sources/experiments/Signika-MM-ext_wght_ext_grad.glyphs, and check if it still exports via FontMake.
Problem with weights:
AssertionError: Location for axis 'Weight' (mapped to 250.0) out of range for 'Signika Light-GRAD' [300.0..700.0]
The Light instances are given a GlyphsApp weight value of 50, while the Light masters have a weight value of 0. Therefore, the instances probably have to change to allow the variable font to export successfully.
Now, this error is happening
AssertionError: Location for axis 'Weight' (mapped to 780.0) out of range for 'Signika Bold' [300.0..700.0]
The Bold instances have a weight values of 920 while the Bold masters have values of 1000. It seems that this is also blocking the export.
Likely, if fonttools and fontmake won't support aligning a variable font to instance values rather than master values, I'll probably have to create a GlyphsApp script that makes masters that match the lightest and boldest instances, then make these into masters, and deletes original masters.
This gets past the Location "out of range" errors, but now cues:
KeyError: 'wght'
I suspect this is due to the generated designspace showing that 0 is the default value for Grade – when I know that 100must be the default for the three-axis master to work. Perhaps, putting the GRAD=100 masters first in the GlyphsApp source will make this the default Grade value?
Yes! It exports, and the Grade default is indeed 100.
The original Signika (currently on Google Fonts) have a "Grade" version that doesn't follow the formalized definitions of grade – keeping the same glyph width metrics while changing letter shapes. When this is fixed, whatever Grade versions we make now won't align with old Grade versions. So, we can't keep the new "Grade" the same as the old "Grade." However, should the overall lettershape difference be the same, or different?
Why to keep it the same
Why to change it
One slightly unfortunate (but probably unimportant) downside of the grade axis is that letters have a very noticeable "wobble" when they're being pulled across their grade range. (As far as I know), this is because the points of contours have to snap to their grid as the letter is interpolated.
Fonts with few Units Per Em (UPM) must round points further when interpolating. This font did have a UPM of 1000, but Dave Crossland is now convincingly suggesting that Variable Fonts take up a standard of 2000 UPM. Could that help reduce the wobble?
It doesn't seem to change much, at least not when tested in FontView.
1000 UPM:
2000 UPM:
WARNING:glyphsLib.builder.builders.UFOBuilder:Non-existent glyph class public.kern2.hyphen found in kerning rules.
Why to change it My first attempt to follow the prior "Grade" shape changes was so subtle, it was barely translating to a variable font. Potentially, if there is a wider range in the Grade axis, it may prove useful in a wider range of contexts.
Update, Nov 26: Actually, I have realized that I was somewhat mistaken. I believe that earlier, I was seeing a comparison between the "Light Negative" instance (weight -15 out of 0–1000) vs the Light master (weight 0 out of 0–1000). However, the actual exported static fonts were "Light Negative" (-15) and default "Light" (50 out of 0–1000). Whereas there are only 0–2 units difference between "Light Negative" and the Light master, there are about 8 units of difference between "Light Negative" and default "Light" instances.




I still am not quite sure whether I should stick to only this amount of difference for grade. Still, it is much more reasonable now than before, because this is actually a difference that would be visible to a human, whereas I initially (mistakenly) thought it would amount to almost nothing.
A wider GRAD range does seem like it might allow for more user flexibility, but keeping the current range would stick to the original design, and it may provide more end-user simplicity (if we accept the claim that it is the correct amount of change for negative typesetting). I plan to test it in a simple web page before coming to a conclusion / asking for opinions.
Okay, that is a good point. What may be the right move for a new font isn't necessarily the right move for a format update to an existing font.Instead of introducing a Grade axis, I'll plan to focus on just making two VFs to replace the existing fonts with minimal-to-no disruption to existing sites.
Another alternative would be, as you said, making a custom "Negative" axis, but this wouldn't be picked up through conventional CSS properties (font controls excluding font-variation-settings), so it seems less useful.
If we want to make a VF containing a Grade axis in the future (in Signika or in a completely different typeface), I have a few scripts and some information that might help out.
I think the 1st VF that has a Negative axis ... and then an 2nd VF that is post-processed out of the 1st, with only the Weight axis
all custom axes including "Grade" axes are also not related to CSS?
I guess TypeDrawers is the best place to post that info.