Support calculating based on fontSize
jesstelford opened this issue · comments
Capsize currently accepts inputs which act as constraints for the algorithm generating the styles. Primarily, those constraints are capHeight
& leading
*:
const styles = capsize({ fontMetrics, leading: 12, capHeight: 10 });
However, my project has a design which specifies a set of fontSize
& lineHeight
combos:
fontSize |
lineHeight |
---|---|
12px |
20px |
14px |
22px |
20px |
28px |
I'd like to be able to use capsize to generate the correct style object given those inputs as constraints:
const styles = capsize({ fontMetrics, fontSize: 12, lineHeight: 20 });
I did a rough draft in #3, but it's out of date and misses a few cases like snapping to grid.
*
(leading
can also be specified as gap
, but I'll treat that as a variation of leading
here)
I did a bit of hacking on this and landed here which lets you do what I wanted above.
NOTE: This may get out of date with Capsize very quickly, so I wouldn't rely on it for anything in production. It's just a proof of concept.
// Modified from https://github.com/seek-oss/capsize/blob/47ad276443609559096f4d95d45de387f97a037b/packages/capsize/src/index.ts
const preventCollapse = 1;
module.exports = function capsize({
leading,
gap,
capHeight,
fontSize,
fontMetrics,
}) {
if (typeof leading !== 'undefined' && typeof gap !== 'undefined') {
throw new Error(
'Only a single line height style can be provided. Please pass either `gap` OR `leading`.'
);
}
if (typeof capHeight !== 'undefined' && typeof fontSize !== 'undefined') {
throw new Error('Please pass either `capHeight` OR `fontSize`, not both.');
}
const capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm;
let specifiedFontSize;
let specifiedCapHeight;
if (typeof fontSize !== 'undefined') {
specifiedFontSize = fontSize;
specifiedCapHeight = fontSize * capHeightScale;
} else {
specifiedFontSize = capHeight / capHeightScale;
specifiedCapHeight = capHeight;
}
const specifiedLineHeight =
typeof gap !== 'undefined' ? specifiedCapHeight + gap : leading;
return createCss({
specifiedLineHeight,
fontSize: specifiedFontSize,
fontMetrics,
});
};
function createCss({ specifiedLineHeight, fontSize, fontMetrics }) {
const capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm;
const toScale = (value) => value / fontSize;
const absoluteDescent = Math.abs(fontMetrics.descent);
const descentScale = absoluteDescent / fontMetrics.unitsPerEm;
const ascentScale = fontMetrics.ascent / fontMetrics.unitsPerEm;
const contentArea = fontMetrics.ascent + absoluteDescent;
const lineHeight = contentArea + fontMetrics.lineGap;
const lineHeightScale = lineHeight / fontMetrics.unitsPerEm;
const lineHeightNormal = lineHeightScale * fontSize;
const hasSpecifiedLineHeight = typeof specifiedLineHeight !== 'undefined';
const specifiedLineHeightOffset =
hasSpecifiedLineHeight && typeof specifiedLineHeight === 'number'
? (lineHeightNormal - specifiedLineHeight) / 2
: 0;
const leadingTrim = (value) =>
value - toScale(specifiedLineHeightOffset) + toScale(preventCollapse);
return {
fontSize: `${fontSize}px`,
...(hasSpecifiedLineHeight && { lineHeight: `${specifiedLineHeight}px` }),
paddingTop: `${preventCollapse}px`,
paddingBottom: `${preventCollapse}px`,
':before': {
content: "''",
marginTop: `-${leadingTrim(ascentScale - capHeightScale)}em`,
display: 'block',
height: 0,
},
':after': {
content: "''",
marginBottom: `-${leadingTrim(descentScale)}em`,
display: 'block',
height: 0,
},
};
}
Would love a font-size based slider
I'm transitioning tailwind-baseline (https://github.com/apkoponen/tailwind-baseline) to capsize and would love this too!
@michaeltaranto mentioned here https://twitter.com/michaeltaranto/status/1280820444275208192 that "it [font-size based calculations] introduces other problems that might not be obvious." Are these issues addressed in @jesstelford's code above or is there something I should be aware of?
@apkoponen I caught up with @jesstelford and feel that the issues i found previously may no longer be a problem.
Planning to include fontSize
support, just working on getting the package published and initial documentation first.
Thanks to @jesstelford for helping out, this is live on the site now