seek-oss / capsize

Flipping how we define typography in CSS.

Home Page:https://seek-oss.github.io/capsize/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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 👏