@capsizecss/core
Advanced tools
Comparing version 3.0.0 to 3.1.0
@@ -9,12 +9,11 @@ 'use strict'; | ||
} | ||
if ('capHeight' in options && 'fontSize' in options) { | ||
throw new Error('Please pass either `capHeight` OR `fontSize`, not both.'); | ||
} | ||
var fontMetrics = options.fontMetrics; | ||
var capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm; | ||
var specifiedFontSize; | ||
var specifiedCapHeight; | ||
const { | ||
fontMetrics | ||
} = options; | ||
const capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm; | ||
let specifiedFontSize; | ||
let specifiedCapHeight; | ||
if ('capHeight' in options) { | ||
@@ -29,5 +28,3 @@ specifiedFontSize = options.capHeight / capHeightScale; | ||
} | ||
var specifiedLineHeight; | ||
let specifiedLineHeight; | ||
if ('lineGap' in options) { | ||
@@ -38,7 +35,6 @@ specifiedLineHeight = specifiedCapHeight + options.lineGap; | ||
} | ||
return { | ||
fontSize: specifiedFontSize, | ||
lineHeight: specifiedLineHeight, | ||
fontMetrics: fontMetrics | ||
fontMetrics | ||
}; | ||
@@ -54,32 +50,27 @@ } | ||
*/ | ||
var round = function round(value) { | ||
return parseFloat(value.toFixed(4)); | ||
}; | ||
const round = value => parseFloat(value.toFixed(4)); | ||
function precomputeValues(options) { | ||
var _normaliseOptions = normaliseOptions(options), | ||
fontSize = _normaliseOptions.fontSize, | ||
lineHeight = _normaliseOptions.lineHeight, | ||
fontMetrics = _normaliseOptions.fontMetrics; | ||
var absoluteDescent = Math.abs(fontMetrics.descent); | ||
var capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm; | ||
var descentScale = absoluteDescent / fontMetrics.unitsPerEm; | ||
var ascentScale = fontMetrics.ascent / fontMetrics.unitsPerEm; | ||
var lineGapScale = fontMetrics.lineGap / fontMetrics.unitsPerEm; | ||
var contentArea = fontMetrics.ascent + fontMetrics.lineGap + absoluteDescent; | ||
var lineHeightScale = contentArea / fontMetrics.unitsPerEm; | ||
var lineHeightNormal = lineHeightScale * fontSize; | ||
var allowForLineHeight = function allowForLineHeight(trim) { | ||
const { | ||
fontSize, | ||
lineHeight, | ||
fontMetrics | ||
} = normaliseOptions(options); | ||
const absoluteDescent = Math.abs(fontMetrics.descent); | ||
const capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm; | ||
const descentScale = absoluteDescent / fontMetrics.unitsPerEm; | ||
const ascentScale = fontMetrics.ascent / fontMetrics.unitsPerEm; | ||
const lineGapScale = fontMetrics.lineGap / fontMetrics.unitsPerEm; | ||
const contentArea = fontMetrics.ascent + fontMetrics.lineGap + absoluteDescent; | ||
const lineHeightScale = contentArea / fontMetrics.unitsPerEm; | ||
const lineHeightNormal = lineHeightScale * fontSize; | ||
const allowForLineHeight = trim => { | ||
if (lineHeight) { | ||
var specifiedLineHeightOffset = (lineHeightNormal - lineHeight) / 2; | ||
const specifiedLineHeightOffset = (lineHeightNormal - lineHeight) / 2; | ||
return trim - specifiedLineHeightOffset / fontSize; | ||
} | ||
return trim; | ||
}; | ||
var capHeightTrim = allowForLineHeight(ascentScale - capHeightScale + lineGapScale / 2) * -1; | ||
var baselineTrim = allowForLineHeight(descentScale + lineGapScale / 2) * -1; | ||
const capHeightTrim = allowForLineHeight(ascentScale - capHeightScale + lineGapScale / 2) * -1; | ||
const baselineTrim = allowForLineHeight(descentScale + lineGapScale / 2) * -1; | ||
return { | ||
@@ -93,10 +84,12 @@ fontSize: "".concat(round(fontSize), "px"), | ||
var _createStyleObject = function _createStyleObject(_ref) { | ||
var lineHeight = _ref.lineHeight, | ||
fontSize = _ref.fontSize, | ||
capHeightTrim = _ref.capHeightTrim, | ||
baselineTrim = _ref.baselineTrim; | ||
const _createStyleObject = _ref => { | ||
let { | ||
lineHeight, | ||
fontSize, | ||
capHeightTrim, | ||
baselineTrim | ||
} = _ref; | ||
return { | ||
fontSize: fontSize, | ||
lineHeight: lineHeight, | ||
fontSize, | ||
lineHeight, | ||
'::before': { | ||
@@ -114,3 +107,2 @@ content: "''", | ||
}; | ||
function createStyleObject(args) { | ||
@@ -120,3 +112,2 @@ if ('capHeightTrim' in args) { | ||
} | ||
return _createStyleObject(precomputeValues(args)); | ||
@@ -130,3 +121,2 @@ } | ||
var key, i; | ||
for (i = 0; i < sourceKeys.length; i++) { | ||
@@ -137,3 +127,2 @@ key = sourceKeys[i]; | ||
} | ||
return target; | ||
@@ -146,6 +135,4 @@ } | ||
var key, i; | ||
if (Object.getOwnPropertySymbols) { | ||
var sourceSymbolKeys = Object.getOwnPropertySymbols(source); | ||
for (i = 0; i < sourceSymbolKeys.length; i++) { | ||
@@ -158,28 +145,240 @@ key = sourceSymbolKeys[i]; | ||
} | ||
return target; | ||
} | ||
var _excluded = ["::before", "::after"]; | ||
const _excluded$1 = ["::before", "::after"]; | ||
function createStyleString(ruleName, options) { | ||
var _createStyleObject = createStyleObject(options), | ||
beforePseudo = _createStyleObject['::before'], | ||
afterPseudo = _createStyleObject['::after'], | ||
rootStyles = _objectWithoutProperties(_createStyleObject, _excluded); | ||
const _createStyleObject = createStyleObject(options), | ||
{ | ||
'::before': beforePseudo, | ||
'::after': afterPseudo | ||
} = _createStyleObject, | ||
rootStyles = _objectWithoutProperties(_createStyleObject, _excluded$1); | ||
const objToCSSRules = (stylesObj, psuedoName) => "\n.".concat(ruleName).concat(psuedoName ? "::".concat(psuedoName) : '', " {\n").concat(Object.keys(stylesObj).map(property => " ".concat(property.replace(/[A-Z]/g, '-$&').toLowerCase(), ": ").concat(stylesObj[property].replace(/'/g, '"'))).join(';\n'), ";\n}"); | ||
return [objToCSSRules(rootStyles), objToCSSRules(beforePseudo, 'before'), objToCSSRules(afterPseudo, 'after')].join('\n'); | ||
} | ||
var objToCSSRules = function objToCSSRules(stylesObj, psuedoName) { | ||
return "\n.".concat(ruleName).concat(psuedoName ? "::".concat(psuedoName) : '', " {\n").concat(Object.keys(stylesObj).map(function (property) { | ||
return " ".concat(property.replace(/[A-Z]/g, '-$&').toLowerCase(), ": ").concat(stylesObj[property].replace(/'/g, '"')); | ||
}).join(';\n'), ";\n}"); | ||
const getCapHeight = _ref => { | ||
let { | ||
fontSize, | ||
fontMetrics | ||
} = _ref; | ||
return round(fontSize * fontMetrics.capHeight / fontMetrics.unitsPerEm); | ||
}; | ||
function _defineProperty(obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
return obj; | ||
} | ||
function ownKeys(object, enumerableOnly) { | ||
var keys = Object.keys(object); | ||
if (Object.getOwnPropertySymbols) { | ||
var symbols = Object.getOwnPropertySymbols(object); | ||
enumerableOnly && (symbols = symbols.filter(function (sym) { | ||
return Object.getOwnPropertyDescriptor(object, sym).enumerable; | ||
})), keys.push.apply(keys, symbols); | ||
} | ||
return keys; | ||
} | ||
function _objectSpread2(target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = null != arguments[i] ? arguments[i] : {}; | ||
i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { | ||
_defineProperty(target, key, source[key]); | ||
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { | ||
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); | ||
}); | ||
} | ||
return target; | ||
} | ||
function _setPrototypeOf(o, p) { | ||
_setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { | ||
o.__proto__ = p; | ||
return o; | ||
}; | ||
return _setPrototypeOf(o, p); | ||
} | ||
return [objToCSSRules(rootStyles), objToCSSRules(beforePseudo, 'before'), objToCSSRules(afterPseudo, 'after')].join('\n'); | ||
function _inherits(subClass, superClass) { | ||
if (typeof superClass !== "function" && superClass !== null) { | ||
throw new TypeError("Super expression must either be null or a function"); | ||
} | ||
subClass.prototype = Object.create(superClass && superClass.prototype, { | ||
constructor: { | ||
value: subClass, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
Object.defineProperty(subClass, "prototype", { | ||
writable: false | ||
}); | ||
if (superClass) _setPrototypeOf(subClass, superClass); | ||
} | ||
var getCapHeight = function getCapHeight(_ref) { | ||
var fontSize = _ref.fontSize, | ||
fontMetrics = _ref.fontMetrics; | ||
return round(fontSize * fontMetrics.capHeight / fontMetrics.unitsPerEm); | ||
function _wrapRegExp() { | ||
_wrapRegExp = function (re, groups) { | ||
return new BabelRegExp(re, void 0, groups); | ||
}; | ||
var _super = RegExp.prototype, | ||
_groups = new WeakMap(); | ||
function BabelRegExp(re, flags, groups) { | ||
var _this = new RegExp(re, flags); | ||
return _groups.set(_this, groups || _groups.get(re)), _setPrototypeOf(_this, BabelRegExp.prototype); | ||
} | ||
function buildGroups(result, re) { | ||
var g = _groups.get(re); | ||
return Object.keys(g).reduce(function (groups, name) { | ||
var i = g[name]; | ||
if ("number" == typeof i) groups[name] = result[i];else { | ||
for (var k = 0; void 0 === result[i[k]] && k + 1 < i.length;) k++; | ||
groups[name] = result[i[k]]; | ||
} | ||
return groups; | ||
}, Object.create(null)); | ||
} | ||
return _inherits(BabelRegExp, RegExp), BabelRegExp.prototype.exec = function (str) { | ||
var result = _super.exec.call(this, str); | ||
return result && (result.groups = buildGroups(result, this)), result; | ||
}, BabelRegExp.prototype[Symbol.replace] = function (str, substitution) { | ||
if ("string" == typeof substitution) { | ||
var groups = _groups.get(this); | ||
return _super[Symbol.replace].call(this, str, substitution.replace(/\$<([^>]+)>/g, function (_, name) { | ||
return "$" + groups[name]; | ||
})); | ||
} | ||
if ("function" == typeof substitution) { | ||
var _this = this; | ||
return _super[Symbol.replace].call(this, str, function () { | ||
var args = arguments; | ||
return "object" != typeof args[args.length - 1] && (args = [].slice.call(args)).push(buildGroups(args, _this)), substitution.apply(this, args); | ||
}); | ||
} | ||
return _super[Symbol.replace].call(this, str, substitution); | ||
}, _wrapRegExp.apply(this, arguments); | ||
} | ||
const _excluded = ["fontFamily", "src"]; | ||
const toPercentString = value => "".concat(round(value * 100), "%"); | ||
const toCssProperty = property => property.replace(/([A-Z])/g, property => "-".concat(property.toLowerCase())); | ||
const calculateOverrideValues = _ref => { | ||
let { | ||
metrics, | ||
fallbackMetrics | ||
} = _ref; | ||
// Calculate size adjust | ||
const preferredFontXAvgRatio = metrics.xWidthAvg / metrics.unitsPerEm; | ||
const fallbackFontXAvgRatio = fallbackMetrics.xWidthAvg / fallbackMetrics.unitsPerEm; | ||
const sizeAdjust = preferredFontXAvgRatio && fallbackFontXAvgRatio ? preferredFontXAvgRatio / fallbackFontXAvgRatio : 1; | ||
const adjustedEmSquare = metrics.unitsPerEm * sizeAdjust; | ||
// Calculate metric overrides for preferred font | ||
const ascentOverride = metrics.ascent / adjustedEmSquare; | ||
const descentOverride = Math.abs(metrics.descent) / adjustedEmSquare; | ||
const lineGapOverride = metrics.lineGap / adjustedEmSquare; | ||
// Conditionally populate font face properties and format to percent | ||
const fontFace = {}; | ||
if (ascentOverride) { | ||
fontFace['ascentOverride'] = toPercentString(ascentOverride); | ||
} | ||
if (descentOverride) { | ||
fontFace['descentOverride'] = toPercentString(descentOverride); | ||
} | ||
if (lineGapOverride) { | ||
fontFace['lineGapOverride'] = toPercentString(lineGapOverride); | ||
} | ||
if (sizeAdjust && sizeAdjust !== 1) { | ||
fontFace['sizeAdjust'] = toPercentString(sizeAdjust); | ||
} | ||
return fontFace; | ||
}; | ||
const quoteIfNeeded = name => { | ||
var _quotedMatch$groups; | ||
const quotedMatch = name.match( /*#__PURE__*/_wrapRegExp(/^['"](.*)['"]$/, { | ||
name: 1 | ||
})); | ||
if (quotedMatch && (_quotedMatch$groups = quotedMatch.groups) !== null && _quotedMatch$groups !== void 0 && _quotedMatch$groups.name) { | ||
// Escape double quotes in middle of name | ||
return "\"".concat(quotedMatch.groups.name.split("\"").join("\""), "\""); | ||
} | ||
if (/^"/.test(name)) { | ||
// Complete double quotes if incomplete and escape double quotes in middle | ||
const [, ...restName] = name; | ||
return "\"".concat(restName.map(x => x === "\"" ? "\"" : x).join(''), "\""); | ||
} | ||
if (!/^[a-zA-Z\d\-_]+$/.test(name)) { | ||
// Wrap in quotes if contains any characters that are not letters, | ||
// numbers, hyphens or underscores | ||
return "\"".concat(name.split("\"").join("\""), "\""); | ||
} | ||
return name; | ||
}; | ||
const toCssString = fontFaces => { | ||
return fontFaces.map(_ref2 => { | ||
let { | ||
'@font-face': { | ||
fontFamily, | ||
src | ||
} | ||
} = _ref2, | ||
restFontFaceProperties = _objectWithoutProperties(_ref2['@font-face'], _excluded); | ||
const fontFace = ['@font-face {', " font-family: ".concat(quoteIfNeeded(fontFamily), ";"), " src: ".concat(src, ";")]; | ||
Object.keys(restFontFaceProperties).forEach(property => { | ||
fontFace.push(" ".concat(toCssProperty(property), ": ").concat(restFontFaceProperties[property], ";")); | ||
}); | ||
fontFace.push('}'); | ||
return fontFace.join('\n'); | ||
}).join('\n'); | ||
}; | ||
function createFontStack(_ref3) { | ||
let [metrics, ...fallbackMetrics] = _ref3; | ||
let optionsArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
const { | ||
fontFaceFormat, | ||
fontFaceProperties | ||
} = _objectSpread2({ | ||
fontFaceFormat: 'styleString' | ||
}, optionsArg); | ||
const { | ||
familyName | ||
} = metrics; | ||
const fontFamilies = [quoteIfNeeded(familyName)]; | ||
const fontFaces = []; | ||
fallbackMetrics.forEach(fallback => { | ||
const fontFamily = "".concat(familyName, " Fallback").concat(fallbackMetrics.length > 1 ? ": ".concat(fallback.familyName) : ''); | ||
fontFamilies.push(quoteIfNeeded(fontFamily)); | ||
fontFaces.push({ | ||
'@font-face': _objectSpread2(_objectSpread2(_objectSpread2({}, fontFaceProperties), {}, { | ||
fontFamily, | ||
src: "local('".concat(fallback.familyName, "')") | ||
}, calculateOverrideValues({ | ||
metrics, | ||
fallbackMetrics: fallback | ||
})), fontFaceProperties !== null && fontFaceProperties !== void 0 && fontFaceProperties.sizeAdjust ? { | ||
sizeAdjust: fontFaceProperties.sizeAdjust | ||
} : {}) | ||
}); | ||
}); | ||
return { | ||
fontFamily: fontFamilies.join(', '), | ||
fontFaces: { | ||
styleString: toCssString(fontFaces), | ||
styleObject: fontFaces | ||
}[fontFaceFormat] | ||
}; | ||
} | ||
exports.createFontStack = createFontStack; | ||
exports.createStyleObject = createStyleObject; | ||
@@ -186,0 +385,0 @@ exports.createStyleString = createStyleString; |
@@ -5,12 +5,11 @@ function normaliseOptions(options) { | ||
} | ||
if ('capHeight' in options && 'fontSize' in options) { | ||
throw new Error('Please pass either `capHeight` OR `fontSize`, not both.'); | ||
} | ||
var fontMetrics = options.fontMetrics; | ||
var capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm; | ||
var specifiedFontSize; | ||
var specifiedCapHeight; | ||
const { | ||
fontMetrics | ||
} = options; | ||
const capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm; | ||
let specifiedFontSize; | ||
let specifiedCapHeight; | ||
if ('capHeight' in options) { | ||
@@ -25,5 +24,3 @@ specifiedFontSize = options.capHeight / capHeightScale; | ||
} | ||
var specifiedLineHeight; | ||
let specifiedLineHeight; | ||
if ('lineGap' in options) { | ||
@@ -34,7 +31,6 @@ specifiedLineHeight = specifiedCapHeight + options.lineGap; | ||
} | ||
return { | ||
fontSize: specifiedFontSize, | ||
lineHeight: specifiedLineHeight, | ||
fontMetrics: fontMetrics | ||
fontMetrics | ||
}; | ||
@@ -50,32 +46,27 @@ } | ||
*/ | ||
var round = function round(value) { | ||
return parseFloat(value.toFixed(4)); | ||
}; | ||
const round = value => parseFloat(value.toFixed(4)); | ||
function precomputeValues(options) { | ||
var _normaliseOptions = normaliseOptions(options), | ||
fontSize = _normaliseOptions.fontSize, | ||
lineHeight = _normaliseOptions.lineHeight, | ||
fontMetrics = _normaliseOptions.fontMetrics; | ||
var absoluteDescent = Math.abs(fontMetrics.descent); | ||
var capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm; | ||
var descentScale = absoluteDescent / fontMetrics.unitsPerEm; | ||
var ascentScale = fontMetrics.ascent / fontMetrics.unitsPerEm; | ||
var lineGapScale = fontMetrics.lineGap / fontMetrics.unitsPerEm; | ||
var contentArea = fontMetrics.ascent + fontMetrics.lineGap + absoluteDescent; | ||
var lineHeightScale = contentArea / fontMetrics.unitsPerEm; | ||
var lineHeightNormal = lineHeightScale * fontSize; | ||
var allowForLineHeight = function allowForLineHeight(trim) { | ||
const { | ||
fontSize, | ||
lineHeight, | ||
fontMetrics | ||
} = normaliseOptions(options); | ||
const absoluteDescent = Math.abs(fontMetrics.descent); | ||
const capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm; | ||
const descentScale = absoluteDescent / fontMetrics.unitsPerEm; | ||
const ascentScale = fontMetrics.ascent / fontMetrics.unitsPerEm; | ||
const lineGapScale = fontMetrics.lineGap / fontMetrics.unitsPerEm; | ||
const contentArea = fontMetrics.ascent + fontMetrics.lineGap + absoluteDescent; | ||
const lineHeightScale = contentArea / fontMetrics.unitsPerEm; | ||
const lineHeightNormal = lineHeightScale * fontSize; | ||
const allowForLineHeight = trim => { | ||
if (lineHeight) { | ||
var specifiedLineHeightOffset = (lineHeightNormal - lineHeight) / 2; | ||
const specifiedLineHeightOffset = (lineHeightNormal - lineHeight) / 2; | ||
return trim - specifiedLineHeightOffset / fontSize; | ||
} | ||
return trim; | ||
}; | ||
var capHeightTrim = allowForLineHeight(ascentScale - capHeightScale + lineGapScale / 2) * -1; | ||
var baselineTrim = allowForLineHeight(descentScale + lineGapScale / 2) * -1; | ||
const capHeightTrim = allowForLineHeight(ascentScale - capHeightScale + lineGapScale / 2) * -1; | ||
const baselineTrim = allowForLineHeight(descentScale + lineGapScale / 2) * -1; | ||
return { | ||
@@ -89,10 +80,12 @@ fontSize: "".concat(round(fontSize), "px"), | ||
var _createStyleObject = function _createStyleObject(_ref) { | ||
var lineHeight = _ref.lineHeight, | ||
fontSize = _ref.fontSize, | ||
capHeightTrim = _ref.capHeightTrim, | ||
baselineTrim = _ref.baselineTrim; | ||
const _createStyleObject = _ref => { | ||
let { | ||
lineHeight, | ||
fontSize, | ||
capHeightTrim, | ||
baselineTrim | ||
} = _ref; | ||
return { | ||
fontSize: fontSize, | ||
lineHeight: lineHeight, | ||
fontSize, | ||
lineHeight, | ||
'::before': { | ||
@@ -110,3 +103,2 @@ content: "''", | ||
}; | ||
function createStyleObject(args) { | ||
@@ -116,3 +108,2 @@ if ('capHeightTrim' in args) { | ||
} | ||
return _createStyleObject(precomputeValues(args)); | ||
@@ -126,3 +117,2 @@ } | ||
var key, i; | ||
for (i = 0; i < sourceKeys.length; i++) { | ||
@@ -133,3 +123,2 @@ key = sourceKeys[i]; | ||
} | ||
return target; | ||
@@ -142,6 +131,4 @@ } | ||
var key, i; | ||
if (Object.getOwnPropertySymbols) { | ||
var sourceSymbolKeys = Object.getOwnPropertySymbols(source); | ||
for (i = 0; i < sourceSymbolKeys.length; i++) { | ||
@@ -154,28 +141,239 @@ key = sourceSymbolKeys[i]; | ||
} | ||
return target; | ||
} | ||
var _excluded = ["::before", "::after"]; | ||
const _excluded$1 = ["::before", "::after"]; | ||
function createStyleString(ruleName, options) { | ||
var _createStyleObject = createStyleObject(options), | ||
beforePseudo = _createStyleObject['::before'], | ||
afterPseudo = _createStyleObject['::after'], | ||
rootStyles = _objectWithoutProperties(_createStyleObject, _excluded); | ||
const _createStyleObject = createStyleObject(options), | ||
{ | ||
'::before': beforePseudo, | ||
'::after': afterPseudo | ||
} = _createStyleObject, | ||
rootStyles = _objectWithoutProperties(_createStyleObject, _excluded$1); | ||
const objToCSSRules = (stylesObj, psuedoName) => "\n.".concat(ruleName).concat(psuedoName ? "::".concat(psuedoName) : '', " {\n").concat(Object.keys(stylesObj).map(property => " ".concat(property.replace(/[A-Z]/g, '-$&').toLowerCase(), ": ").concat(stylesObj[property].replace(/'/g, '"'))).join(';\n'), ";\n}"); | ||
return [objToCSSRules(rootStyles), objToCSSRules(beforePseudo, 'before'), objToCSSRules(afterPseudo, 'after')].join('\n'); | ||
} | ||
var objToCSSRules = function objToCSSRules(stylesObj, psuedoName) { | ||
return "\n.".concat(ruleName).concat(psuedoName ? "::".concat(psuedoName) : '', " {\n").concat(Object.keys(stylesObj).map(function (property) { | ||
return " ".concat(property.replace(/[A-Z]/g, '-$&').toLowerCase(), ": ").concat(stylesObj[property].replace(/'/g, '"')); | ||
}).join(';\n'), ";\n}"); | ||
const getCapHeight = _ref => { | ||
let { | ||
fontSize, | ||
fontMetrics | ||
} = _ref; | ||
return round(fontSize * fontMetrics.capHeight / fontMetrics.unitsPerEm); | ||
}; | ||
function _defineProperty(obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
return obj; | ||
} | ||
function ownKeys(object, enumerableOnly) { | ||
var keys = Object.keys(object); | ||
if (Object.getOwnPropertySymbols) { | ||
var symbols = Object.getOwnPropertySymbols(object); | ||
enumerableOnly && (symbols = symbols.filter(function (sym) { | ||
return Object.getOwnPropertyDescriptor(object, sym).enumerable; | ||
})), keys.push.apply(keys, symbols); | ||
} | ||
return keys; | ||
} | ||
function _objectSpread2(target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = null != arguments[i] ? arguments[i] : {}; | ||
i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { | ||
_defineProperty(target, key, source[key]); | ||
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { | ||
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); | ||
}); | ||
} | ||
return target; | ||
} | ||
function _setPrototypeOf(o, p) { | ||
_setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { | ||
o.__proto__ = p; | ||
return o; | ||
}; | ||
return _setPrototypeOf(o, p); | ||
} | ||
return [objToCSSRules(rootStyles), objToCSSRules(beforePseudo, 'before'), objToCSSRules(afterPseudo, 'after')].join('\n'); | ||
function _inherits(subClass, superClass) { | ||
if (typeof superClass !== "function" && superClass !== null) { | ||
throw new TypeError("Super expression must either be null or a function"); | ||
} | ||
subClass.prototype = Object.create(superClass && superClass.prototype, { | ||
constructor: { | ||
value: subClass, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
Object.defineProperty(subClass, "prototype", { | ||
writable: false | ||
}); | ||
if (superClass) _setPrototypeOf(subClass, superClass); | ||
} | ||
var getCapHeight = function getCapHeight(_ref) { | ||
var fontSize = _ref.fontSize, | ||
fontMetrics = _ref.fontMetrics; | ||
return round(fontSize * fontMetrics.capHeight / fontMetrics.unitsPerEm); | ||
function _wrapRegExp() { | ||
_wrapRegExp = function (re, groups) { | ||
return new BabelRegExp(re, void 0, groups); | ||
}; | ||
var _super = RegExp.prototype, | ||
_groups = new WeakMap(); | ||
function BabelRegExp(re, flags, groups) { | ||
var _this = new RegExp(re, flags); | ||
return _groups.set(_this, groups || _groups.get(re)), _setPrototypeOf(_this, BabelRegExp.prototype); | ||
} | ||
function buildGroups(result, re) { | ||
var g = _groups.get(re); | ||
return Object.keys(g).reduce(function (groups, name) { | ||
var i = g[name]; | ||
if ("number" == typeof i) groups[name] = result[i];else { | ||
for (var k = 0; void 0 === result[i[k]] && k + 1 < i.length;) k++; | ||
groups[name] = result[i[k]]; | ||
} | ||
return groups; | ||
}, Object.create(null)); | ||
} | ||
return _inherits(BabelRegExp, RegExp), BabelRegExp.prototype.exec = function (str) { | ||
var result = _super.exec.call(this, str); | ||
return result && (result.groups = buildGroups(result, this)), result; | ||
}, BabelRegExp.prototype[Symbol.replace] = function (str, substitution) { | ||
if ("string" == typeof substitution) { | ||
var groups = _groups.get(this); | ||
return _super[Symbol.replace].call(this, str, substitution.replace(/\$<([^>]+)>/g, function (_, name) { | ||
return "$" + groups[name]; | ||
})); | ||
} | ||
if ("function" == typeof substitution) { | ||
var _this = this; | ||
return _super[Symbol.replace].call(this, str, function () { | ||
var args = arguments; | ||
return "object" != typeof args[args.length - 1] && (args = [].slice.call(args)).push(buildGroups(args, _this)), substitution.apply(this, args); | ||
}); | ||
} | ||
return _super[Symbol.replace].call(this, str, substitution); | ||
}, _wrapRegExp.apply(this, arguments); | ||
} | ||
const _excluded = ["fontFamily", "src"]; | ||
const toPercentString = value => "".concat(round(value * 100), "%"); | ||
const toCssProperty = property => property.replace(/([A-Z])/g, property => "-".concat(property.toLowerCase())); | ||
const calculateOverrideValues = _ref => { | ||
let { | ||
metrics, | ||
fallbackMetrics | ||
} = _ref; | ||
// Calculate size adjust | ||
const preferredFontXAvgRatio = metrics.xWidthAvg / metrics.unitsPerEm; | ||
const fallbackFontXAvgRatio = fallbackMetrics.xWidthAvg / fallbackMetrics.unitsPerEm; | ||
const sizeAdjust = preferredFontXAvgRatio && fallbackFontXAvgRatio ? preferredFontXAvgRatio / fallbackFontXAvgRatio : 1; | ||
const adjustedEmSquare = metrics.unitsPerEm * sizeAdjust; | ||
// Calculate metric overrides for preferred font | ||
const ascentOverride = metrics.ascent / adjustedEmSquare; | ||
const descentOverride = Math.abs(metrics.descent) / adjustedEmSquare; | ||
const lineGapOverride = metrics.lineGap / adjustedEmSquare; | ||
// Conditionally populate font face properties and format to percent | ||
const fontFace = {}; | ||
if (ascentOverride) { | ||
fontFace['ascentOverride'] = toPercentString(ascentOverride); | ||
} | ||
if (descentOverride) { | ||
fontFace['descentOverride'] = toPercentString(descentOverride); | ||
} | ||
if (lineGapOverride) { | ||
fontFace['lineGapOverride'] = toPercentString(lineGapOverride); | ||
} | ||
if (sizeAdjust && sizeAdjust !== 1) { | ||
fontFace['sizeAdjust'] = toPercentString(sizeAdjust); | ||
} | ||
return fontFace; | ||
}; | ||
const quoteIfNeeded = name => { | ||
var _quotedMatch$groups; | ||
const quotedMatch = name.match( /*#__PURE__*/_wrapRegExp(/^['"](.*)['"]$/, { | ||
name: 1 | ||
})); | ||
if (quotedMatch && (_quotedMatch$groups = quotedMatch.groups) !== null && _quotedMatch$groups !== void 0 && _quotedMatch$groups.name) { | ||
// Escape double quotes in middle of name | ||
return "\"".concat(quotedMatch.groups.name.split("\"").join("\""), "\""); | ||
} | ||
if (/^"/.test(name)) { | ||
// Complete double quotes if incomplete and escape double quotes in middle | ||
const [, ...restName] = name; | ||
return "\"".concat(restName.map(x => x === "\"" ? "\"" : x).join(''), "\""); | ||
} | ||
if (!/^[a-zA-Z\d\-_]+$/.test(name)) { | ||
// Wrap in quotes if contains any characters that are not letters, | ||
// numbers, hyphens or underscores | ||
return "\"".concat(name.split("\"").join("\""), "\""); | ||
} | ||
return name; | ||
}; | ||
const toCssString = fontFaces => { | ||
return fontFaces.map(_ref2 => { | ||
let { | ||
'@font-face': { | ||
fontFamily, | ||
src | ||
} | ||
} = _ref2, | ||
restFontFaceProperties = _objectWithoutProperties(_ref2['@font-face'], _excluded); | ||
const fontFace = ['@font-face {', " font-family: ".concat(quoteIfNeeded(fontFamily), ";"), " src: ".concat(src, ";")]; | ||
Object.keys(restFontFaceProperties).forEach(property => { | ||
fontFace.push(" ".concat(toCssProperty(property), ": ").concat(restFontFaceProperties[property], ";")); | ||
}); | ||
fontFace.push('}'); | ||
return fontFace.join('\n'); | ||
}).join('\n'); | ||
}; | ||
function createFontStack(_ref3) { | ||
let [metrics, ...fallbackMetrics] = _ref3; | ||
let optionsArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
const { | ||
fontFaceFormat, | ||
fontFaceProperties | ||
} = _objectSpread2({ | ||
fontFaceFormat: 'styleString' | ||
}, optionsArg); | ||
const { | ||
familyName | ||
} = metrics; | ||
const fontFamilies = [quoteIfNeeded(familyName)]; | ||
const fontFaces = []; | ||
fallbackMetrics.forEach(fallback => { | ||
const fontFamily = "".concat(familyName, " Fallback").concat(fallbackMetrics.length > 1 ? ": ".concat(fallback.familyName) : ''); | ||
fontFamilies.push(quoteIfNeeded(fontFamily)); | ||
fontFaces.push({ | ||
'@font-face': _objectSpread2(_objectSpread2(_objectSpread2({}, fontFaceProperties), {}, { | ||
fontFamily, | ||
src: "local('".concat(fallback.familyName, "')") | ||
}, calculateOverrideValues({ | ||
metrics, | ||
fallbackMetrics: fallback | ||
})), fontFaceProperties !== null && fontFaceProperties !== void 0 && fontFaceProperties.sizeAdjust ? { | ||
sizeAdjust: fontFaceProperties.sizeAdjust | ||
} : {}) | ||
}); | ||
}); | ||
return { | ||
fontFamily: fontFamilies.join(', '), | ||
fontFaces: { | ||
styleString: toCssString(fontFaces), | ||
styleObject: fontFaces | ||
}[fontFaceFormat] | ||
}; | ||
} | ||
export { createStyleObject, createStyleString, getCapHeight, precomputeValues }; | ||
export { createFontStack, createStyleObject, createStyleString, getCapHeight, precomputeValues }; |
@@ -9,12 +9,11 @@ 'use strict'; | ||
} | ||
if ('capHeight' in options && 'fontSize' in options) { | ||
throw new Error('Please pass either `capHeight` OR `fontSize`, not both.'); | ||
} | ||
var fontMetrics = options.fontMetrics; | ||
var capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm; | ||
var specifiedFontSize; | ||
var specifiedCapHeight; | ||
const { | ||
fontMetrics | ||
} = options; | ||
const capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm; | ||
let specifiedFontSize; | ||
let specifiedCapHeight; | ||
if ('capHeight' in options) { | ||
@@ -29,5 +28,3 @@ specifiedFontSize = options.capHeight / capHeightScale; | ||
} | ||
var specifiedLineHeight; | ||
let specifiedLineHeight; | ||
if ('lineGap' in options) { | ||
@@ -38,7 +35,6 @@ specifiedLineHeight = specifiedCapHeight + options.lineGap; | ||
} | ||
return { | ||
fontSize: specifiedFontSize, | ||
lineHeight: specifiedLineHeight, | ||
fontMetrics: fontMetrics | ||
fontMetrics | ||
}; | ||
@@ -54,32 +50,27 @@ } | ||
*/ | ||
var round = function round(value) { | ||
return parseFloat(value.toFixed(4)); | ||
}; | ||
const round = value => parseFloat(value.toFixed(4)); | ||
function precomputeValues(options) { | ||
var _normaliseOptions = normaliseOptions(options), | ||
fontSize = _normaliseOptions.fontSize, | ||
lineHeight = _normaliseOptions.lineHeight, | ||
fontMetrics = _normaliseOptions.fontMetrics; | ||
var absoluteDescent = Math.abs(fontMetrics.descent); | ||
var capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm; | ||
var descentScale = absoluteDescent / fontMetrics.unitsPerEm; | ||
var ascentScale = fontMetrics.ascent / fontMetrics.unitsPerEm; | ||
var lineGapScale = fontMetrics.lineGap / fontMetrics.unitsPerEm; | ||
var contentArea = fontMetrics.ascent + fontMetrics.lineGap + absoluteDescent; | ||
var lineHeightScale = contentArea / fontMetrics.unitsPerEm; | ||
var lineHeightNormal = lineHeightScale * fontSize; | ||
var allowForLineHeight = function allowForLineHeight(trim) { | ||
const { | ||
fontSize, | ||
lineHeight, | ||
fontMetrics | ||
} = normaliseOptions(options); | ||
const absoluteDescent = Math.abs(fontMetrics.descent); | ||
const capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm; | ||
const descentScale = absoluteDescent / fontMetrics.unitsPerEm; | ||
const ascentScale = fontMetrics.ascent / fontMetrics.unitsPerEm; | ||
const lineGapScale = fontMetrics.lineGap / fontMetrics.unitsPerEm; | ||
const contentArea = fontMetrics.ascent + fontMetrics.lineGap + absoluteDescent; | ||
const lineHeightScale = contentArea / fontMetrics.unitsPerEm; | ||
const lineHeightNormal = lineHeightScale * fontSize; | ||
const allowForLineHeight = trim => { | ||
if (lineHeight) { | ||
var specifiedLineHeightOffset = (lineHeightNormal - lineHeight) / 2; | ||
const specifiedLineHeightOffset = (lineHeightNormal - lineHeight) / 2; | ||
return trim - specifiedLineHeightOffset / fontSize; | ||
} | ||
return trim; | ||
}; | ||
var capHeightTrim = allowForLineHeight(ascentScale - capHeightScale + lineGapScale / 2) * -1; | ||
var baselineTrim = allowForLineHeight(descentScale + lineGapScale / 2) * -1; | ||
const capHeightTrim = allowForLineHeight(ascentScale - capHeightScale + lineGapScale / 2) * -1; | ||
const baselineTrim = allowForLineHeight(descentScale + lineGapScale / 2) * -1; | ||
return { | ||
@@ -93,10 +84,12 @@ fontSize: "".concat(round(fontSize), "px"), | ||
var _createStyleObject = function _createStyleObject(_ref) { | ||
var lineHeight = _ref.lineHeight, | ||
fontSize = _ref.fontSize, | ||
capHeightTrim = _ref.capHeightTrim, | ||
baselineTrim = _ref.baselineTrim; | ||
const _createStyleObject = _ref => { | ||
let { | ||
lineHeight, | ||
fontSize, | ||
capHeightTrim, | ||
baselineTrim | ||
} = _ref; | ||
return { | ||
fontSize: fontSize, | ||
lineHeight: lineHeight, | ||
fontSize, | ||
lineHeight, | ||
'::before': { | ||
@@ -114,3 +107,2 @@ content: "''", | ||
}; | ||
function createStyleObject(args) { | ||
@@ -120,3 +112,2 @@ if ('capHeightTrim' in args) { | ||
} | ||
return _createStyleObject(precomputeValues(args)); | ||
@@ -130,3 +121,2 @@ } | ||
var key, i; | ||
for (i = 0; i < sourceKeys.length; i++) { | ||
@@ -137,3 +127,2 @@ key = sourceKeys[i]; | ||
} | ||
return target; | ||
@@ -146,6 +135,4 @@ } | ||
var key, i; | ||
if (Object.getOwnPropertySymbols) { | ||
var sourceSymbolKeys = Object.getOwnPropertySymbols(source); | ||
for (i = 0; i < sourceSymbolKeys.length; i++) { | ||
@@ -158,28 +145,240 @@ key = sourceSymbolKeys[i]; | ||
} | ||
return target; | ||
} | ||
var _excluded = ["::before", "::after"]; | ||
const _excluded$1 = ["::before", "::after"]; | ||
function createStyleString(ruleName, options) { | ||
var _createStyleObject = createStyleObject(options), | ||
beforePseudo = _createStyleObject['::before'], | ||
afterPseudo = _createStyleObject['::after'], | ||
rootStyles = _objectWithoutProperties(_createStyleObject, _excluded); | ||
const _createStyleObject = createStyleObject(options), | ||
{ | ||
'::before': beforePseudo, | ||
'::after': afterPseudo | ||
} = _createStyleObject, | ||
rootStyles = _objectWithoutProperties(_createStyleObject, _excluded$1); | ||
const objToCSSRules = (stylesObj, psuedoName) => "\n.".concat(ruleName).concat(psuedoName ? "::".concat(psuedoName) : '', " {\n").concat(Object.keys(stylesObj).map(property => " ".concat(property.replace(/[A-Z]/g, '-$&').toLowerCase(), ": ").concat(stylesObj[property].replace(/'/g, '"'))).join(';\n'), ";\n}"); | ||
return [objToCSSRules(rootStyles), objToCSSRules(beforePseudo, 'before'), objToCSSRules(afterPseudo, 'after')].join('\n'); | ||
} | ||
var objToCSSRules = function objToCSSRules(stylesObj, psuedoName) { | ||
return "\n.".concat(ruleName).concat(psuedoName ? "::".concat(psuedoName) : '', " {\n").concat(Object.keys(stylesObj).map(function (property) { | ||
return " ".concat(property.replace(/[A-Z]/g, '-$&').toLowerCase(), ": ").concat(stylesObj[property].replace(/'/g, '"')); | ||
}).join(';\n'), ";\n}"); | ||
const getCapHeight = _ref => { | ||
let { | ||
fontSize, | ||
fontMetrics | ||
} = _ref; | ||
return round(fontSize * fontMetrics.capHeight / fontMetrics.unitsPerEm); | ||
}; | ||
function _defineProperty(obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
return obj; | ||
} | ||
function ownKeys(object, enumerableOnly) { | ||
var keys = Object.keys(object); | ||
if (Object.getOwnPropertySymbols) { | ||
var symbols = Object.getOwnPropertySymbols(object); | ||
enumerableOnly && (symbols = symbols.filter(function (sym) { | ||
return Object.getOwnPropertyDescriptor(object, sym).enumerable; | ||
})), keys.push.apply(keys, symbols); | ||
} | ||
return keys; | ||
} | ||
function _objectSpread2(target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = null != arguments[i] ? arguments[i] : {}; | ||
i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { | ||
_defineProperty(target, key, source[key]); | ||
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { | ||
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); | ||
}); | ||
} | ||
return target; | ||
} | ||
function _setPrototypeOf(o, p) { | ||
_setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { | ||
o.__proto__ = p; | ||
return o; | ||
}; | ||
return _setPrototypeOf(o, p); | ||
} | ||
return [objToCSSRules(rootStyles), objToCSSRules(beforePseudo, 'before'), objToCSSRules(afterPseudo, 'after')].join('\n'); | ||
function _inherits(subClass, superClass) { | ||
if (typeof superClass !== "function" && superClass !== null) { | ||
throw new TypeError("Super expression must either be null or a function"); | ||
} | ||
subClass.prototype = Object.create(superClass && superClass.prototype, { | ||
constructor: { | ||
value: subClass, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
Object.defineProperty(subClass, "prototype", { | ||
writable: false | ||
}); | ||
if (superClass) _setPrototypeOf(subClass, superClass); | ||
} | ||
var getCapHeight = function getCapHeight(_ref) { | ||
var fontSize = _ref.fontSize, | ||
fontMetrics = _ref.fontMetrics; | ||
return round(fontSize * fontMetrics.capHeight / fontMetrics.unitsPerEm); | ||
function _wrapRegExp() { | ||
_wrapRegExp = function (re, groups) { | ||
return new BabelRegExp(re, void 0, groups); | ||
}; | ||
var _super = RegExp.prototype, | ||
_groups = new WeakMap(); | ||
function BabelRegExp(re, flags, groups) { | ||
var _this = new RegExp(re, flags); | ||
return _groups.set(_this, groups || _groups.get(re)), _setPrototypeOf(_this, BabelRegExp.prototype); | ||
} | ||
function buildGroups(result, re) { | ||
var g = _groups.get(re); | ||
return Object.keys(g).reduce(function (groups, name) { | ||
var i = g[name]; | ||
if ("number" == typeof i) groups[name] = result[i];else { | ||
for (var k = 0; void 0 === result[i[k]] && k + 1 < i.length;) k++; | ||
groups[name] = result[i[k]]; | ||
} | ||
return groups; | ||
}, Object.create(null)); | ||
} | ||
return _inherits(BabelRegExp, RegExp), BabelRegExp.prototype.exec = function (str) { | ||
var result = _super.exec.call(this, str); | ||
return result && (result.groups = buildGroups(result, this)), result; | ||
}, BabelRegExp.prototype[Symbol.replace] = function (str, substitution) { | ||
if ("string" == typeof substitution) { | ||
var groups = _groups.get(this); | ||
return _super[Symbol.replace].call(this, str, substitution.replace(/\$<([^>]+)>/g, function (_, name) { | ||
return "$" + groups[name]; | ||
})); | ||
} | ||
if ("function" == typeof substitution) { | ||
var _this = this; | ||
return _super[Symbol.replace].call(this, str, function () { | ||
var args = arguments; | ||
return "object" != typeof args[args.length - 1] && (args = [].slice.call(args)).push(buildGroups(args, _this)), substitution.apply(this, args); | ||
}); | ||
} | ||
return _super[Symbol.replace].call(this, str, substitution); | ||
}, _wrapRegExp.apply(this, arguments); | ||
} | ||
const _excluded = ["fontFamily", "src"]; | ||
const toPercentString = value => "".concat(round(value * 100), "%"); | ||
const toCssProperty = property => property.replace(/([A-Z])/g, property => "-".concat(property.toLowerCase())); | ||
const calculateOverrideValues = _ref => { | ||
let { | ||
metrics, | ||
fallbackMetrics | ||
} = _ref; | ||
// Calculate size adjust | ||
const preferredFontXAvgRatio = metrics.xWidthAvg / metrics.unitsPerEm; | ||
const fallbackFontXAvgRatio = fallbackMetrics.xWidthAvg / fallbackMetrics.unitsPerEm; | ||
const sizeAdjust = preferredFontXAvgRatio && fallbackFontXAvgRatio ? preferredFontXAvgRatio / fallbackFontXAvgRatio : 1; | ||
const adjustedEmSquare = metrics.unitsPerEm * sizeAdjust; | ||
// Calculate metric overrides for preferred font | ||
const ascentOverride = metrics.ascent / adjustedEmSquare; | ||
const descentOverride = Math.abs(metrics.descent) / adjustedEmSquare; | ||
const lineGapOverride = metrics.lineGap / adjustedEmSquare; | ||
// Conditionally populate font face properties and format to percent | ||
const fontFace = {}; | ||
if (ascentOverride) { | ||
fontFace['ascentOverride'] = toPercentString(ascentOverride); | ||
} | ||
if (descentOverride) { | ||
fontFace['descentOverride'] = toPercentString(descentOverride); | ||
} | ||
if (lineGapOverride) { | ||
fontFace['lineGapOverride'] = toPercentString(lineGapOverride); | ||
} | ||
if (sizeAdjust && sizeAdjust !== 1) { | ||
fontFace['sizeAdjust'] = toPercentString(sizeAdjust); | ||
} | ||
return fontFace; | ||
}; | ||
const quoteIfNeeded = name => { | ||
var _quotedMatch$groups; | ||
const quotedMatch = name.match( /*#__PURE__*/_wrapRegExp(/^['"](.*)['"]$/, { | ||
name: 1 | ||
})); | ||
if (quotedMatch && (_quotedMatch$groups = quotedMatch.groups) !== null && _quotedMatch$groups !== void 0 && _quotedMatch$groups.name) { | ||
// Escape double quotes in middle of name | ||
return "\"".concat(quotedMatch.groups.name.split("\"").join("\""), "\""); | ||
} | ||
if (/^"/.test(name)) { | ||
// Complete double quotes if incomplete and escape double quotes in middle | ||
const [, ...restName] = name; | ||
return "\"".concat(restName.map(x => x === "\"" ? "\"" : x).join(''), "\""); | ||
} | ||
if (!/^[a-zA-Z\d\-_]+$/.test(name)) { | ||
// Wrap in quotes if contains any characters that are not letters, | ||
// numbers, hyphens or underscores | ||
return "\"".concat(name.split("\"").join("\""), "\""); | ||
} | ||
return name; | ||
}; | ||
const toCssString = fontFaces => { | ||
return fontFaces.map(_ref2 => { | ||
let { | ||
'@font-face': { | ||
fontFamily, | ||
src | ||
} | ||
} = _ref2, | ||
restFontFaceProperties = _objectWithoutProperties(_ref2['@font-face'], _excluded); | ||
const fontFace = ['@font-face {', " font-family: ".concat(quoteIfNeeded(fontFamily), ";"), " src: ".concat(src, ";")]; | ||
Object.keys(restFontFaceProperties).forEach(property => { | ||
fontFace.push(" ".concat(toCssProperty(property), ": ").concat(restFontFaceProperties[property], ";")); | ||
}); | ||
fontFace.push('}'); | ||
return fontFace.join('\n'); | ||
}).join('\n'); | ||
}; | ||
function createFontStack(_ref3) { | ||
let [metrics, ...fallbackMetrics] = _ref3; | ||
let optionsArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
const { | ||
fontFaceFormat, | ||
fontFaceProperties | ||
} = _objectSpread2({ | ||
fontFaceFormat: 'styleString' | ||
}, optionsArg); | ||
const { | ||
familyName | ||
} = metrics; | ||
const fontFamilies = [quoteIfNeeded(familyName)]; | ||
const fontFaces = []; | ||
fallbackMetrics.forEach(fallback => { | ||
const fontFamily = "".concat(familyName, " Fallback").concat(fallbackMetrics.length > 1 ? ": ".concat(fallback.familyName) : ''); | ||
fontFamilies.push(quoteIfNeeded(fontFamily)); | ||
fontFaces.push({ | ||
'@font-face': _objectSpread2(_objectSpread2(_objectSpread2({}, fontFaceProperties), {}, { | ||
fontFamily, | ||
src: "local('".concat(fallback.familyName, "')") | ||
}, calculateOverrideValues({ | ||
metrics, | ||
fallbackMetrics: fallback | ||
})), fontFaceProperties !== null && fontFaceProperties !== void 0 && fontFaceProperties.sizeAdjust ? { | ||
sizeAdjust: fontFaceProperties.sizeAdjust | ||
} : {}) | ||
}); | ||
}); | ||
return { | ||
fontFamily: fontFamilies.join(', '), | ||
fontFaces: { | ||
styleString: toCssString(fontFaces), | ||
styleObject: fontFaces | ||
}[fontFaceFormat] | ||
}; | ||
} | ||
exports.createFontStack = createFontStack; | ||
exports.createStyleObject = createStyleObject; | ||
@@ -186,0 +385,0 @@ exports.createStyleString = createStyleString; |
@@ -9,12 +9,11 @@ 'use strict'; | ||
} | ||
if ('capHeight' in options && 'fontSize' in options) { | ||
throw new Error('Please pass either `capHeight` OR `fontSize`, not both.'); | ||
} | ||
var fontMetrics = options.fontMetrics; | ||
var capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm; | ||
var specifiedFontSize; | ||
var specifiedCapHeight; | ||
const { | ||
fontMetrics | ||
} = options; | ||
const capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm; | ||
let specifiedFontSize; | ||
let specifiedCapHeight; | ||
if ('capHeight' in options) { | ||
@@ -29,5 +28,3 @@ specifiedFontSize = options.capHeight / capHeightScale; | ||
} | ||
var specifiedLineHeight; | ||
let specifiedLineHeight; | ||
if ('lineGap' in options) { | ||
@@ -38,7 +35,6 @@ specifiedLineHeight = specifiedCapHeight + options.lineGap; | ||
} | ||
return { | ||
fontSize: specifiedFontSize, | ||
lineHeight: specifiedLineHeight, | ||
fontMetrics: fontMetrics | ||
fontMetrics | ||
}; | ||
@@ -54,32 +50,27 @@ } | ||
*/ | ||
var round = function round(value) { | ||
return parseFloat(value.toFixed(4)); | ||
}; | ||
const round = value => parseFloat(value.toFixed(4)); | ||
function precomputeValues(options) { | ||
var _normaliseOptions = normaliseOptions(options), | ||
fontSize = _normaliseOptions.fontSize, | ||
lineHeight = _normaliseOptions.lineHeight, | ||
fontMetrics = _normaliseOptions.fontMetrics; | ||
var absoluteDescent = Math.abs(fontMetrics.descent); | ||
var capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm; | ||
var descentScale = absoluteDescent / fontMetrics.unitsPerEm; | ||
var ascentScale = fontMetrics.ascent / fontMetrics.unitsPerEm; | ||
var lineGapScale = fontMetrics.lineGap / fontMetrics.unitsPerEm; | ||
var contentArea = fontMetrics.ascent + fontMetrics.lineGap + absoluteDescent; | ||
var lineHeightScale = contentArea / fontMetrics.unitsPerEm; | ||
var lineHeightNormal = lineHeightScale * fontSize; | ||
var allowForLineHeight = function allowForLineHeight(trim) { | ||
const { | ||
fontSize, | ||
lineHeight, | ||
fontMetrics | ||
} = normaliseOptions(options); | ||
const absoluteDescent = Math.abs(fontMetrics.descent); | ||
const capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm; | ||
const descentScale = absoluteDescent / fontMetrics.unitsPerEm; | ||
const ascentScale = fontMetrics.ascent / fontMetrics.unitsPerEm; | ||
const lineGapScale = fontMetrics.lineGap / fontMetrics.unitsPerEm; | ||
const contentArea = fontMetrics.ascent + fontMetrics.lineGap + absoluteDescent; | ||
const lineHeightScale = contentArea / fontMetrics.unitsPerEm; | ||
const lineHeightNormal = lineHeightScale * fontSize; | ||
const allowForLineHeight = trim => { | ||
if (lineHeight) { | ||
var specifiedLineHeightOffset = (lineHeightNormal - lineHeight) / 2; | ||
const specifiedLineHeightOffset = (lineHeightNormal - lineHeight) / 2; | ||
return trim - specifiedLineHeightOffset / fontSize; | ||
} | ||
return trim; | ||
}; | ||
var capHeightTrim = allowForLineHeight(ascentScale - capHeightScale + lineGapScale / 2) * -1; | ||
var baselineTrim = allowForLineHeight(descentScale + lineGapScale / 2) * -1; | ||
const capHeightTrim = allowForLineHeight(ascentScale - capHeightScale + lineGapScale / 2) * -1; | ||
const baselineTrim = allowForLineHeight(descentScale + lineGapScale / 2) * -1; | ||
return { | ||
@@ -93,10 +84,12 @@ fontSize: "".concat(round(fontSize), "px"), | ||
var _createStyleObject = function _createStyleObject(_ref) { | ||
var lineHeight = _ref.lineHeight, | ||
fontSize = _ref.fontSize, | ||
capHeightTrim = _ref.capHeightTrim, | ||
baselineTrim = _ref.baselineTrim; | ||
const _createStyleObject = _ref => { | ||
let { | ||
lineHeight, | ||
fontSize, | ||
capHeightTrim, | ||
baselineTrim | ||
} = _ref; | ||
return { | ||
fontSize: fontSize, | ||
lineHeight: lineHeight, | ||
fontSize, | ||
lineHeight, | ||
'::before': { | ||
@@ -114,3 +107,2 @@ content: "''", | ||
}; | ||
function createStyleObject(args) { | ||
@@ -120,3 +112,2 @@ if ('capHeightTrim' in args) { | ||
} | ||
return _createStyleObject(precomputeValues(args)); | ||
@@ -130,3 +121,2 @@ } | ||
var key, i; | ||
for (i = 0; i < sourceKeys.length; i++) { | ||
@@ -137,3 +127,2 @@ key = sourceKeys[i]; | ||
} | ||
return target; | ||
@@ -146,6 +135,4 @@ } | ||
var key, i; | ||
if (Object.getOwnPropertySymbols) { | ||
var sourceSymbolKeys = Object.getOwnPropertySymbols(source); | ||
for (i = 0; i < sourceSymbolKeys.length; i++) { | ||
@@ -158,28 +145,240 @@ key = sourceSymbolKeys[i]; | ||
} | ||
return target; | ||
} | ||
var _excluded = ["::before", "::after"]; | ||
const _excluded$1 = ["::before", "::after"]; | ||
function createStyleString(ruleName, options) { | ||
var _createStyleObject = createStyleObject(options), | ||
beforePseudo = _createStyleObject['::before'], | ||
afterPseudo = _createStyleObject['::after'], | ||
rootStyles = _objectWithoutProperties(_createStyleObject, _excluded); | ||
const _createStyleObject = createStyleObject(options), | ||
{ | ||
'::before': beforePseudo, | ||
'::after': afterPseudo | ||
} = _createStyleObject, | ||
rootStyles = _objectWithoutProperties(_createStyleObject, _excluded$1); | ||
const objToCSSRules = (stylesObj, psuedoName) => "\n.".concat(ruleName).concat(psuedoName ? "::".concat(psuedoName) : '', " {\n").concat(Object.keys(stylesObj).map(property => " ".concat(property.replace(/[A-Z]/g, '-$&').toLowerCase(), ": ").concat(stylesObj[property].replace(/'/g, '"'))).join(';\n'), ";\n}"); | ||
return [objToCSSRules(rootStyles), objToCSSRules(beforePseudo, 'before'), objToCSSRules(afterPseudo, 'after')].join('\n'); | ||
} | ||
var objToCSSRules = function objToCSSRules(stylesObj, psuedoName) { | ||
return "\n.".concat(ruleName).concat(psuedoName ? "::".concat(psuedoName) : '', " {\n").concat(Object.keys(stylesObj).map(function (property) { | ||
return " ".concat(property.replace(/[A-Z]/g, '-$&').toLowerCase(), ": ").concat(stylesObj[property].replace(/'/g, '"')); | ||
}).join(';\n'), ";\n}"); | ||
const getCapHeight = _ref => { | ||
let { | ||
fontSize, | ||
fontMetrics | ||
} = _ref; | ||
return round(fontSize * fontMetrics.capHeight / fontMetrics.unitsPerEm); | ||
}; | ||
function _defineProperty(obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
return obj; | ||
} | ||
function ownKeys(object, enumerableOnly) { | ||
var keys = Object.keys(object); | ||
if (Object.getOwnPropertySymbols) { | ||
var symbols = Object.getOwnPropertySymbols(object); | ||
enumerableOnly && (symbols = symbols.filter(function (sym) { | ||
return Object.getOwnPropertyDescriptor(object, sym).enumerable; | ||
})), keys.push.apply(keys, symbols); | ||
} | ||
return keys; | ||
} | ||
function _objectSpread2(target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = null != arguments[i] ? arguments[i] : {}; | ||
i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { | ||
_defineProperty(target, key, source[key]); | ||
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { | ||
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); | ||
}); | ||
} | ||
return target; | ||
} | ||
function _setPrototypeOf(o, p) { | ||
_setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { | ||
o.__proto__ = p; | ||
return o; | ||
}; | ||
return _setPrototypeOf(o, p); | ||
} | ||
return [objToCSSRules(rootStyles), objToCSSRules(beforePseudo, 'before'), objToCSSRules(afterPseudo, 'after')].join('\n'); | ||
function _inherits(subClass, superClass) { | ||
if (typeof superClass !== "function" && superClass !== null) { | ||
throw new TypeError("Super expression must either be null or a function"); | ||
} | ||
subClass.prototype = Object.create(superClass && superClass.prototype, { | ||
constructor: { | ||
value: subClass, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
Object.defineProperty(subClass, "prototype", { | ||
writable: false | ||
}); | ||
if (superClass) _setPrototypeOf(subClass, superClass); | ||
} | ||
var getCapHeight = function getCapHeight(_ref) { | ||
var fontSize = _ref.fontSize, | ||
fontMetrics = _ref.fontMetrics; | ||
return round(fontSize * fontMetrics.capHeight / fontMetrics.unitsPerEm); | ||
function _wrapRegExp() { | ||
_wrapRegExp = function (re, groups) { | ||
return new BabelRegExp(re, void 0, groups); | ||
}; | ||
var _super = RegExp.prototype, | ||
_groups = new WeakMap(); | ||
function BabelRegExp(re, flags, groups) { | ||
var _this = new RegExp(re, flags); | ||
return _groups.set(_this, groups || _groups.get(re)), _setPrototypeOf(_this, BabelRegExp.prototype); | ||
} | ||
function buildGroups(result, re) { | ||
var g = _groups.get(re); | ||
return Object.keys(g).reduce(function (groups, name) { | ||
var i = g[name]; | ||
if ("number" == typeof i) groups[name] = result[i];else { | ||
for (var k = 0; void 0 === result[i[k]] && k + 1 < i.length;) k++; | ||
groups[name] = result[i[k]]; | ||
} | ||
return groups; | ||
}, Object.create(null)); | ||
} | ||
return _inherits(BabelRegExp, RegExp), BabelRegExp.prototype.exec = function (str) { | ||
var result = _super.exec.call(this, str); | ||
return result && (result.groups = buildGroups(result, this)), result; | ||
}, BabelRegExp.prototype[Symbol.replace] = function (str, substitution) { | ||
if ("string" == typeof substitution) { | ||
var groups = _groups.get(this); | ||
return _super[Symbol.replace].call(this, str, substitution.replace(/\$<([^>]+)>/g, function (_, name) { | ||
return "$" + groups[name]; | ||
})); | ||
} | ||
if ("function" == typeof substitution) { | ||
var _this = this; | ||
return _super[Symbol.replace].call(this, str, function () { | ||
var args = arguments; | ||
return "object" != typeof args[args.length - 1] && (args = [].slice.call(args)).push(buildGroups(args, _this)), substitution.apply(this, args); | ||
}); | ||
} | ||
return _super[Symbol.replace].call(this, str, substitution); | ||
}, _wrapRegExp.apply(this, arguments); | ||
} | ||
const _excluded = ["fontFamily", "src"]; | ||
const toPercentString = value => "".concat(round(value * 100), "%"); | ||
const toCssProperty = property => property.replace(/([A-Z])/g, property => "-".concat(property.toLowerCase())); | ||
const calculateOverrideValues = _ref => { | ||
let { | ||
metrics, | ||
fallbackMetrics | ||
} = _ref; | ||
// Calculate size adjust | ||
const preferredFontXAvgRatio = metrics.xWidthAvg / metrics.unitsPerEm; | ||
const fallbackFontXAvgRatio = fallbackMetrics.xWidthAvg / fallbackMetrics.unitsPerEm; | ||
const sizeAdjust = preferredFontXAvgRatio && fallbackFontXAvgRatio ? preferredFontXAvgRatio / fallbackFontXAvgRatio : 1; | ||
const adjustedEmSquare = metrics.unitsPerEm * sizeAdjust; | ||
// Calculate metric overrides for preferred font | ||
const ascentOverride = metrics.ascent / adjustedEmSquare; | ||
const descentOverride = Math.abs(metrics.descent) / adjustedEmSquare; | ||
const lineGapOverride = metrics.lineGap / adjustedEmSquare; | ||
// Conditionally populate font face properties and format to percent | ||
const fontFace = {}; | ||
if (ascentOverride) { | ||
fontFace['ascentOverride'] = toPercentString(ascentOverride); | ||
} | ||
if (descentOverride) { | ||
fontFace['descentOverride'] = toPercentString(descentOverride); | ||
} | ||
if (lineGapOverride) { | ||
fontFace['lineGapOverride'] = toPercentString(lineGapOverride); | ||
} | ||
if (sizeAdjust && sizeAdjust !== 1) { | ||
fontFace['sizeAdjust'] = toPercentString(sizeAdjust); | ||
} | ||
return fontFace; | ||
}; | ||
const quoteIfNeeded = name => { | ||
var _quotedMatch$groups; | ||
const quotedMatch = name.match( /*#__PURE__*/_wrapRegExp(/^['"](.*)['"]$/, { | ||
name: 1 | ||
})); | ||
if (quotedMatch && (_quotedMatch$groups = quotedMatch.groups) !== null && _quotedMatch$groups !== void 0 && _quotedMatch$groups.name) { | ||
// Escape double quotes in middle of name | ||
return "\"".concat(quotedMatch.groups.name.split("\"").join("\""), "\""); | ||
} | ||
if (/^"/.test(name)) { | ||
// Complete double quotes if incomplete and escape double quotes in middle | ||
const [, ...restName] = name; | ||
return "\"".concat(restName.map(x => x === "\"" ? "\"" : x).join(''), "\""); | ||
} | ||
if (!/^[a-zA-Z\d\-_]+$/.test(name)) { | ||
// Wrap in quotes if contains any characters that are not letters, | ||
// numbers, hyphens or underscores | ||
return "\"".concat(name.split("\"").join("\""), "\""); | ||
} | ||
return name; | ||
}; | ||
const toCssString = fontFaces => { | ||
return fontFaces.map(_ref2 => { | ||
let { | ||
'@font-face': { | ||
fontFamily, | ||
src | ||
} | ||
} = _ref2, | ||
restFontFaceProperties = _objectWithoutProperties(_ref2['@font-face'], _excluded); | ||
const fontFace = ['@font-face {', " font-family: ".concat(quoteIfNeeded(fontFamily), ";"), " src: ".concat(src, ";")]; | ||
Object.keys(restFontFaceProperties).forEach(property => { | ||
fontFace.push(" ".concat(toCssProperty(property), ": ").concat(restFontFaceProperties[property], ";")); | ||
}); | ||
fontFace.push('}'); | ||
return fontFace.join('\n'); | ||
}).join('\n'); | ||
}; | ||
function createFontStack(_ref3) { | ||
let [metrics, ...fallbackMetrics] = _ref3; | ||
let optionsArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
const { | ||
fontFaceFormat, | ||
fontFaceProperties | ||
} = _objectSpread2({ | ||
fontFaceFormat: 'styleString' | ||
}, optionsArg); | ||
const { | ||
familyName | ||
} = metrics; | ||
const fontFamilies = [quoteIfNeeded(familyName)]; | ||
const fontFaces = []; | ||
fallbackMetrics.forEach(fallback => { | ||
const fontFamily = "".concat(familyName, " Fallback").concat(fallbackMetrics.length > 1 ? ": ".concat(fallback.familyName) : ''); | ||
fontFamilies.push(quoteIfNeeded(fontFamily)); | ||
fontFaces.push({ | ||
'@font-face': _objectSpread2(_objectSpread2(_objectSpread2({}, fontFaceProperties), {}, { | ||
fontFamily, | ||
src: "local('".concat(fallback.familyName, "')") | ||
}, calculateOverrideValues({ | ||
metrics, | ||
fallbackMetrics: fallback | ||
})), fontFaceProperties !== null && fontFaceProperties !== void 0 && fontFaceProperties.sizeAdjust ? { | ||
sizeAdjust: fontFaceProperties.sizeAdjust | ||
} : {}) | ||
}); | ||
}); | ||
return { | ||
fontFamily: fontFamilies.join(', '), | ||
fontFaces: { | ||
styleString: toCssString(fontFaces), | ||
styleObject: fontFaces | ||
}[fontFaceFormat] | ||
}; | ||
} | ||
exports.createFontStack = createFontStack; | ||
exports.createStyleObject = createStyleObject; | ||
@@ -186,0 +385,0 @@ exports.createStyleString = createStyleString; |
@@ -5,12 +5,11 @@ function normaliseOptions(options) { | ||
} | ||
if ('capHeight' in options && 'fontSize' in options) { | ||
throw new Error('Please pass either `capHeight` OR `fontSize`, not both.'); | ||
} | ||
var fontMetrics = options.fontMetrics; | ||
var capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm; | ||
var specifiedFontSize; | ||
var specifiedCapHeight; | ||
const { | ||
fontMetrics | ||
} = options; | ||
const capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm; | ||
let specifiedFontSize; | ||
let specifiedCapHeight; | ||
if ('capHeight' in options) { | ||
@@ -25,5 +24,3 @@ specifiedFontSize = options.capHeight / capHeightScale; | ||
} | ||
var specifiedLineHeight; | ||
let specifiedLineHeight; | ||
if ('lineGap' in options) { | ||
@@ -34,7 +31,6 @@ specifiedLineHeight = specifiedCapHeight + options.lineGap; | ||
} | ||
return { | ||
fontSize: specifiedFontSize, | ||
lineHeight: specifiedLineHeight, | ||
fontMetrics: fontMetrics | ||
fontMetrics | ||
}; | ||
@@ -50,32 +46,27 @@ } | ||
*/ | ||
var round = function round(value) { | ||
return parseFloat(value.toFixed(4)); | ||
}; | ||
const round = value => parseFloat(value.toFixed(4)); | ||
function precomputeValues(options) { | ||
var _normaliseOptions = normaliseOptions(options), | ||
fontSize = _normaliseOptions.fontSize, | ||
lineHeight = _normaliseOptions.lineHeight, | ||
fontMetrics = _normaliseOptions.fontMetrics; | ||
var absoluteDescent = Math.abs(fontMetrics.descent); | ||
var capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm; | ||
var descentScale = absoluteDescent / fontMetrics.unitsPerEm; | ||
var ascentScale = fontMetrics.ascent / fontMetrics.unitsPerEm; | ||
var lineGapScale = fontMetrics.lineGap / fontMetrics.unitsPerEm; | ||
var contentArea = fontMetrics.ascent + fontMetrics.lineGap + absoluteDescent; | ||
var lineHeightScale = contentArea / fontMetrics.unitsPerEm; | ||
var lineHeightNormal = lineHeightScale * fontSize; | ||
var allowForLineHeight = function allowForLineHeight(trim) { | ||
const { | ||
fontSize, | ||
lineHeight, | ||
fontMetrics | ||
} = normaliseOptions(options); | ||
const absoluteDescent = Math.abs(fontMetrics.descent); | ||
const capHeightScale = fontMetrics.capHeight / fontMetrics.unitsPerEm; | ||
const descentScale = absoluteDescent / fontMetrics.unitsPerEm; | ||
const ascentScale = fontMetrics.ascent / fontMetrics.unitsPerEm; | ||
const lineGapScale = fontMetrics.lineGap / fontMetrics.unitsPerEm; | ||
const contentArea = fontMetrics.ascent + fontMetrics.lineGap + absoluteDescent; | ||
const lineHeightScale = contentArea / fontMetrics.unitsPerEm; | ||
const lineHeightNormal = lineHeightScale * fontSize; | ||
const allowForLineHeight = trim => { | ||
if (lineHeight) { | ||
var specifiedLineHeightOffset = (lineHeightNormal - lineHeight) / 2; | ||
const specifiedLineHeightOffset = (lineHeightNormal - lineHeight) / 2; | ||
return trim - specifiedLineHeightOffset / fontSize; | ||
} | ||
return trim; | ||
}; | ||
var capHeightTrim = allowForLineHeight(ascentScale - capHeightScale + lineGapScale / 2) * -1; | ||
var baselineTrim = allowForLineHeight(descentScale + lineGapScale / 2) * -1; | ||
const capHeightTrim = allowForLineHeight(ascentScale - capHeightScale + lineGapScale / 2) * -1; | ||
const baselineTrim = allowForLineHeight(descentScale + lineGapScale / 2) * -1; | ||
return { | ||
@@ -89,10 +80,12 @@ fontSize: "".concat(round(fontSize), "px"), | ||
var _createStyleObject = function _createStyleObject(_ref) { | ||
var lineHeight = _ref.lineHeight, | ||
fontSize = _ref.fontSize, | ||
capHeightTrim = _ref.capHeightTrim, | ||
baselineTrim = _ref.baselineTrim; | ||
const _createStyleObject = _ref => { | ||
let { | ||
lineHeight, | ||
fontSize, | ||
capHeightTrim, | ||
baselineTrim | ||
} = _ref; | ||
return { | ||
fontSize: fontSize, | ||
lineHeight: lineHeight, | ||
fontSize, | ||
lineHeight, | ||
'::before': { | ||
@@ -110,3 +103,2 @@ content: "''", | ||
}; | ||
function createStyleObject(args) { | ||
@@ -116,3 +108,2 @@ if ('capHeightTrim' in args) { | ||
} | ||
return _createStyleObject(precomputeValues(args)); | ||
@@ -126,3 +117,2 @@ } | ||
var key, i; | ||
for (i = 0; i < sourceKeys.length; i++) { | ||
@@ -133,3 +123,2 @@ key = sourceKeys[i]; | ||
} | ||
return target; | ||
@@ -142,6 +131,4 @@ } | ||
var key, i; | ||
if (Object.getOwnPropertySymbols) { | ||
var sourceSymbolKeys = Object.getOwnPropertySymbols(source); | ||
for (i = 0; i < sourceSymbolKeys.length; i++) { | ||
@@ -154,28 +141,239 @@ key = sourceSymbolKeys[i]; | ||
} | ||
return target; | ||
} | ||
var _excluded = ["::before", "::after"]; | ||
const _excluded$1 = ["::before", "::after"]; | ||
function createStyleString(ruleName, options) { | ||
var _createStyleObject = createStyleObject(options), | ||
beforePseudo = _createStyleObject['::before'], | ||
afterPseudo = _createStyleObject['::after'], | ||
rootStyles = _objectWithoutProperties(_createStyleObject, _excluded); | ||
const _createStyleObject = createStyleObject(options), | ||
{ | ||
'::before': beforePseudo, | ||
'::after': afterPseudo | ||
} = _createStyleObject, | ||
rootStyles = _objectWithoutProperties(_createStyleObject, _excluded$1); | ||
const objToCSSRules = (stylesObj, psuedoName) => "\n.".concat(ruleName).concat(psuedoName ? "::".concat(psuedoName) : '', " {\n").concat(Object.keys(stylesObj).map(property => " ".concat(property.replace(/[A-Z]/g, '-$&').toLowerCase(), ": ").concat(stylesObj[property].replace(/'/g, '"'))).join(';\n'), ";\n}"); | ||
return [objToCSSRules(rootStyles), objToCSSRules(beforePseudo, 'before'), objToCSSRules(afterPseudo, 'after')].join('\n'); | ||
} | ||
var objToCSSRules = function objToCSSRules(stylesObj, psuedoName) { | ||
return "\n.".concat(ruleName).concat(psuedoName ? "::".concat(psuedoName) : '', " {\n").concat(Object.keys(stylesObj).map(function (property) { | ||
return " ".concat(property.replace(/[A-Z]/g, '-$&').toLowerCase(), ": ").concat(stylesObj[property].replace(/'/g, '"')); | ||
}).join(';\n'), ";\n}"); | ||
const getCapHeight = _ref => { | ||
let { | ||
fontSize, | ||
fontMetrics | ||
} = _ref; | ||
return round(fontSize * fontMetrics.capHeight / fontMetrics.unitsPerEm); | ||
}; | ||
function _defineProperty(obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
return obj; | ||
} | ||
function ownKeys(object, enumerableOnly) { | ||
var keys = Object.keys(object); | ||
if (Object.getOwnPropertySymbols) { | ||
var symbols = Object.getOwnPropertySymbols(object); | ||
enumerableOnly && (symbols = symbols.filter(function (sym) { | ||
return Object.getOwnPropertyDescriptor(object, sym).enumerable; | ||
})), keys.push.apply(keys, symbols); | ||
} | ||
return keys; | ||
} | ||
function _objectSpread2(target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = null != arguments[i] ? arguments[i] : {}; | ||
i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { | ||
_defineProperty(target, key, source[key]); | ||
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { | ||
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); | ||
}); | ||
} | ||
return target; | ||
} | ||
function _setPrototypeOf(o, p) { | ||
_setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { | ||
o.__proto__ = p; | ||
return o; | ||
}; | ||
return _setPrototypeOf(o, p); | ||
} | ||
return [objToCSSRules(rootStyles), objToCSSRules(beforePseudo, 'before'), objToCSSRules(afterPseudo, 'after')].join('\n'); | ||
function _inherits(subClass, superClass) { | ||
if (typeof superClass !== "function" && superClass !== null) { | ||
throw new TypeError("Super expression must either be null or a function"); | ||
} | ||
subClass.prototype = Object.create(superClass && superClass.prototype, { | ||
constructor: { | ||
value: subClass, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
Object.defineProperty(subClass, "prototype", { | ||
writable: false | ||
}); | ||
if (superClass) _setPrototypeOf(subClass, superClass); | ||
} | ||
var getCapHeight = function getCapHeight(_ref) { | ||
var fontSize = _ref.fontSize, | ||
fontMetrics = _ref.fontMetrics; | ||
return round(fontSize * fontMetrics.capHeight / fontMetrics.unitsPerEm); | ||
function _wrapRegExp() { | ||
_wrapRegExp = function (re, groups) { | ||
return new BabelRegExp(re, void 0, groups); | ||
}; | ||
var _super = RegExp.prototype, | ||
_groups = new WeakMap(); | ||
function BabelRegExp(re, flags, groups) { | ||
var _this = new RegExp(re, flags); | ||
return _groups.set(_this, groups || _groups.get(re)), _setPrototypeOf(_this, BabelRegExp.prototype); | ||
} | ||
function buildGroups(result, re) { | ||
var g = _groups.get(re); | ||
return Object.keys(g).reduce(function (groups, name) { | ||
var i = g[name]; | ||
if ("number" == typeof i) groups[name] = result[i];else { | ||
for (var k = 0; void 0 === result[i[k]] && k + 1 < i.length;) k++; | ||
groups[name] = result[i[k]]; | ||
} | ||
return groups; | ||
}, Object.create(null)); | ||
} | ||
return _inherits(BabelRegExp, RegExp), BabelRegExp.prototype.exec = function (str) { | ||
var result = _super.exec.call(this, str); | ||
return result && (result.groups = buildGroups(result, this)), result; | ||
}, BabelRegExp.prototype[Symbol.replace] = function (str, substitution) { | ||
if ("string" == typeof substitution) { | ||
var groups = _groups.get(this); | ||
return _super[Symbol.replace].call(this, str, substitution.replace(/\$<([^>]+)>/g, function (_, name) { | ||
return "$" + groups[name]; | ||
})); | ||
} | ||
if ("function" == typeof substitution) { | ||
var _this = this; | ||
return _super[Symbol.replace].call(this, str, function () { | ||
var args = arguments; | ||
return "object" != typeof args[args.length - 1] && (args = [].slice.call(args)).push(buildGroups(args, _this)), substitution.apply(this, args); | ||
}); | ||
} | ||
return _super[Symbol.replace].call(this, str, substitution); | ||
}, _wrapRegExp.apply(this, arguments); | ||
} | ||
const _excluded = ["fontFamily", "src"]; | ||
const toPercentString = value => "".concat(round(value * 100), "%"); | ||
const toCssProperty = property => property.replace(/([A-Z])/g, property => "-".concat(property.toLowerCase())); | ||
const calculateOverrideValues = _ref => { | ||
let { | ||
metrics, | ||
fallbackMetrics | ||
} = _ref; | ||
// Calculate size adjust | ||
const preferredFontXAvgRatio = metrics.xWidthAvg / metrics.unitsPerEm; | ||
const fallbackFontXAvgRatio = fallbackMetrics.xWidthAvg / fallbackMetrics.unitsPerEm; | ||
const sizeAdjust = preferredFontXAvgRatio && fallbackFontXAvgRatio ? preferredFontXAvgRatio / fallbackFontXAvgRatio : 1; | ||
const adjustedEmSquare = metrics.unitsPerEm * sizeAdjust; | ||
// Calculate metric overrides for preferred font | ||
const ascentOverride = metrics.ascent / adjustedEmSquare; | ||
const descentOverride = Math.abs(metrics.descent) / adjustedEmSquare; | ||
const lineGapOverride = metrics.lineGap / adjustedEmSquare; | ||
// Conditionally populate font face properties and format to percent | ||
const fontFace = {}; | ||
if (ascentOverride) { | ||
fontFace['ascentOverride'] = toPercentString(ascentOverride); | ||
} | ||
if (descentOverride) { | ||
fontFace['descentOverride'] = toPercentString(descentOverride); | ||
} | ||
if (lineGapOverride) { | ||
fontFace['lineGapOverride'] = toPercentString(lineGapOverride); | ||
} | ||
if (sizeAdjust && sizeAdjust !== 1) { | ||
fontFace['sizeAdjust'] = toPercentString(sizeAdjust); | ||
} | ||
return fontFace; | ||
}; | ||
const quoteIfNeeded = name => { | ||
var _quotedMatch$groups; | ||
const quotedMatch = name.match( /*#__PURE__*/_wrapRegExp(/^['"](.*)['"]$/, { | ||
name: 1 | ||
})); | ||
if (quotedMatch && (_quotedMatch$groups = quotedMatch.groups) !== null && _quotedMatch$groups !== void 0 && _quotedMatch$groups.name) { | ||
// Escape double quotes in middle of name | ||
return "\"".concat(quotedMatch.groups.name.split("\"").join("\""), "\""); | ||
} | ||
if (/^"/.test(name)) { | ||
// Complete double quotes if incomplete and escape double quotes in middle | ||
const [, ...restName] = name; | ||
return "\"".concat(restName.map(x => x === "\"" ? "\"" : x).join(''), "\""); | ||
} | ||
if (!/^[a-zA-Z\d\-_]+$/.test(name)) { | ||
// Wrap in quotes if contains any characters that are not letters, | ||
// numbers, hyphens or underscores | ||
return "\"".concat(name.split("\"").join("\""), "\""); | ||
} | ||
return name; | ||
}; | ||
const toCssString = fontFaces => { | ||
return fontFaces.map(_ref2 => { | ||
let { | ||
'@font-face': { | ||
fontFamily, | ||
src | ||
} | ||
} = _ref2, | ||
restFontFaceProperties = _objectWithoutProperties(_ref2['@font-face'], _excluded); | ||
const fontFace = ['@font-face {', " font-family: ".concat(quoteIfNeeded(fontFamily), ";"), " src: ".concat(src, ";")]; | ||
Object.keys(restFontFaceProperties).forEach(property => { | ||
fontFace.push(" ".concat(toCssProperty(property), ": ").concat(restFontFaceProperties[property], ";")); | ||
}); | ||
fontFace.push('}'); | ||
return fontFace.join('\n'); | ||
}).join('\n'); | ||
}; | ||
function createFontStack(_ref3) { | ||
let [metrics, ...fallbackMetrics] = _ref3; | ||
let optionsArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
const { | ||
fontFaceFormat, | ||
fontFaceProperties | ||
} = _objectSpread2({ | ||
fontFaceFormat: 'styleString' | ||
}, optionsArg); | ||
const { | ||
familyName | ||
} = metrics; | ||
const fontFamilies = [quoteIfNeeded(familyName)]; | ||
const fontFaces = []; | ||
fallbackMetrics.forEach(fallback => { | ||
const fontFamily = "".concat(familyName, " Fallback").concat(fallbackMetrics.length > 1 ? ": ".concat(fallback.familyName) : ''); | ||
fontFamilies.push(quoteIfNeeded(fontFamily)); | ||
fontFaces.push({ | ||
'@font-face': _objectSpread2(_objectSpread2(_objectSpread2({}, fontFaceProperties), {}, { | ||
fontFamily, | ||
src: "local('".concat(fallback.familyName, "')") | ||
}, calculateOverrideValues({ | ||
metrics, | ||
fallbackMetrics: fallback | ||
})), fontFaceProperties !== null && fontFaceProperties !== void 0 && fontFaceProperties.sizeAdjust ? { | ||
sizeAdjust: fontFaceProperties.sizeAdjust | ||
} : {}) | ||
}); | ||
}); | ||
return { | ||
fontFamily: fontFamilies.join(', '), | ||
fontFaces: { | ||
styleString: toCssString(fontFaces), | ||
styleObject: fontFaces | ||
}[fontFaceFormat] | ||
}; | ||
} | ||
export { createStyleObject, createStyleString, getCapHeight, precomputeValues }; | ||
export { createFontStack, createStyleObject, createStyleString, getCapHeight, precomputeValues }; |
@@ -0,0 +0,0 @@ import type { CapsizeOptions, ComputedValues } from './types'; |
import { createStyleObject } from './createStyleObject'; | ||
export declare function createStyleString(ruleName: string, options: Parameters<typeof createStyleObject>[0]): string; |
import { FontMetrics } from './types'; | ||
export declare const getCapHeight: ({ fontSize, fontMetrics, }: { | ||
fontSize: number; | ||
fontMetrics: FontMetrics; | ||
fontMetrics: Pick<FontMetrics, 'capHeight' | 'unitsPerEm'>; | ||
}) => number; |
@@ -5,2 +5,3 @@ export { createStyleObject } from './createStyleObject'; | ||
export { getCapHeight } from './getCapHeight'; | ||
export { createFontStack } from './createFontStack'; | ||
export type { FontMetrics } from './types'; |
@@ -5,3 +5,9 @@ import { CapsizeOptions } from './types'; | ||
lineHeight: number | undefined; | ||
fontMetrics: import("./types").FontMetrics; | ||
fontMetrics: { | ||
capHeight: number; | ||
ascent: number; | ||
descent: number; | ||
lineGap: number; | ||
unitsPerEm: number; | ||
}; | ||
}; |
import { ComputedValues, CapsizeOptions } from './types'; | ||
export declare function precomputeValues(options: CapsizeOptions): ComputedValues; |
export declare const round: (value: number) => number; |
export interface FontMetrics { | ||
/** The font family name as authored by font creator */ | ||
familyName: string; | ||
/** | ||
* The style of the font: serif, sans-serif, monospace, display, or handwriting. | ||
* | ||
* (Optional as only availble for metrics from the `@capsizecss/metrics` package. Value not extractable from font data tables.) | ||
*/ | ||
category?: string; | ||
/** The height of the ascenders above baseline */ | ||
ascent: number; | ||
/** The descent of the descenders below baseline */ | ||
descent: number; | ||
/** The amount of space included between lines */ | ||
lineGap: number; | ||
/** The size of the font’s internal coordinate grid */ | ||
unitsPerEm: number; | ||
/** The height of capital letters above the baseline */ | ||
capHeight: number; | ||
/** The height of the main body of lower case letters above baseline */ | ||
xHeight: number; | ||
/** The average width of lowercase characters (currently derived from latin character frequencies in English language) */ | ||
xWidthAvg: number; | ||
} | ||
@@ -17,6 +34,7 @@ export declare type ComputedValues = { | ||
}; | ||
declare type FontMetricsForTrim = Pick<FontMetrics, 'ascent' | 'descent' | 'capHeight' | 'lineGap' | 'unitsPerEm'>; | ||
declare type CapHeightWithLeading = { | ||
capHeight: number; | ||
leading?: number; | ||
fontMetrics: FontMetrics; | ||
fontMetrics: FontMetricsForTrim; | ||
} & NotComputedValues; | ||
@@ -26,3 +44,3 @@ declare type CapHeightWithLineGap = { | ||
lineGap: number; | ||
fontMetrics: FontMetrics; | ||
fontMetrics: FontMetricsForTrim; | ||
} & NotComputedValues; | ||
@@ -32,3 +50,3 @@ declare type FontSizeWithLeading = { | ||
leading?: number; | ||
fontMetrics: FontMetrics; | ||
fontMetrics: FontMetricsForTrim; | ||
} & Omit<NotComputedValues, 'fontSize'>; | ||
@@ -38,5 +56,5 @@ declare type FontSizeWithLineGap = { | ||
lineGap: number; | ||
fontMetrics: FontMetrics; | ||
fontMetrics: FontMetricsForTrim; | ||
} & Omit<NotComputedValues, 'fontSize'>; | ||
export declare type CapsizeOptions = CapHeightWithLineGap | CapHeightWithLeading | FontSizeWithLineGap | FontSizeWithLeading; | ||
export {}; |
{ | ||
"name": "@capsizecss/core", | ||
"version": "3.0.0", | ||
"version": "3.1.0", | ||
"description": "Flipping how we define typography", | ||
@@ -37,9 +37,20 @@ "main": "dist/capsizecss-core.cjs.js", | ||
"line gap", | ||
"leading" | ||
"leading", | ||
"fallback font", | ||
"font metrics", | ||
"ascent override", | ||
"descent override", | ||
"line gap override", | ||
"size adjust", | ||
"content layout shift", | ||
"cls" | ||
], | ||
"license": "MIT", | ||
"dependencies": {}, | ||
"dependencies": { | ||
"csstype": "^3.1.1" | ||
}, | ||
"devDependencies": { | ||
"@babel/core": "^7.19.6", | ||
"@emotion/css": "^11.1.3" | ||
} | ||
} | ||
} |
257
README.md
@@ -1,4 +0,4 @@ | ||
<img src="https://raw.githubusercontent.com/seek-oss/capsize/HEAD/images/capsize-header.png" alt="Capsize" title="Capsize" width="443px" /> | ||
<img src="https://raw.githubusercontent.com/seek-oss/capsize/HEAD/images/capsize-header.png#gh-light-mode-only" alt="Capsize" title="Capsize" width="443px" /> | ||
<img src="https://raw.githubusercontent.com/seek-oss/capsize/HEAD/images/capsize-header-inverted.png#gh-dark-mode-only" alt="Capsize" title="Capsize" width="443px" /> | ||
<br/> | ||
<br/> | ||
@@ -9,6 +9,9 @@ > Capsize makes the sizing and layout of text as predictable as every other element on the screen. | ||
<br/> | ||
```bash | ||
npm install @capsizecss/core | ||
``` | ||
- [Installation](#installation) | ||
- [Usage](#usage) | ||
- [createStyleObject](#createstyleobject) | ||
- [createStyleString](#createstylestring) | ||
- [Options](#options) | ||
@@ -19,19 +22,13 @@ - [Text size](#text-size) | ||
- [Core](#core) | ||
- [createFontStack](#createfontstack) | ||
- [Usage in CSS stylesheet](#usage-in-css-stylesheet-or-a-style-tag) | ||
- [Usage with CSS-in-JS frameworks](#usage-with-css-in-js-frameworks) | ||
- [Additional `font-face` properties](#providing-additional-font-face-properties) | ||
- [precomputeValues](#precomputevalues) | ||
- [getCapHeight](#getcapheight) | ||
- [Metrics](#metrics) | ||
- [Unpack](#unpack) | ||
- [Integrations](#integrations) | ||
- [vanilla-extract](packages/vanilla-extract/README.md) | ||
<br/> | ||
## Installation | ||
Install the core package: | ||
```bash | ||
npm install @capsizecss/core | ||
``` | ||
<br/> | ||
## Usage | ||
@@ -61,2 +58,17 @@ | ||
Note: It is recommended that you install the [@capsizecss/metrics](packages/metrics/README.md) package and import the metrics from there: | ||
```ts | ||
import { createStyleObject } from '@capsizecss/core'; | ||
import arialMetrics from '@capsizecss/metrics/arial'; | ||
const capsizeStyles = createStyleObject({ | ||
fontSize: 16, | ||
leading: 24, | ||
fontMetrics: arialMetrics, | ||
}); | ||
``` | ||
See the [fontMetrics](#font-metrics) option documented below for more ways to obtain these metrics. | ||
2. Apply styles to the text element, for example via the `css` prop. | ||
@@ -83,2 +95,3 @@ | ||
import { createStyleString } from '@capsizecss/core'; | ||
import arialMetrics from '@capsizecss/metrics/arial'; | ||
@@ -88,9 +101,3 @@ const capsizedStyleRule = createStyleString('capsizedText', { | ||
leading: 24, | ||
fontMetrics: { | ||
capHeight: 700, | ||
ascent: 1058, | ||
descent: -291, | ||
lineGap: 0, | ||
unitsPerEm: 1000, | ||
}, | ||
fontMetrics: arialMetrics, | ||
}); | ||
@@ -112,4 +119,2 @@ ``` | ||
<br/> | ||
## Options | ||
@@ -155,23 +160,173 @@ | ||
This metadata is extracted from the metrics tables inside the font itself. You can use [the Capsize website](https://seek-oss.github.io/capsize/) to find these by selecting a font and referencing `Metrics` tab in step 3. | ||
This metadata is extracted from the metrics tables inside the font itself. There are a number of ways to find this information: | ||
<br/> | ||
- If using a Google Font or system font, install the [@capsizecss/metrics](packages/metrics/README.md) package and import the metrics by name. For example: | ||
```ts | ||
import arialMetrics from '@capsizecss/metrics/arial'; | ||
``` | ||
- If using a font from a file, install the [@capsizecss/unpack](packages/unpack/README.md) package and extract the metrics from the font file directly. For example: | ||
```ts | ||
import { fromFile } from '@capsizecss/unpack'; | ||
const metrics = await fromFile(filePath); | ||
``` | ||
- Or, use [the Capsize website](https://seek-oss.github.io/capsize/) to find these by selecting a font and referencing `Metrics` tab in step 3. | ||
## Core | ||
The core package also provides access to lower level values for a specific font and font size combination. | ||
The core package also provides a few other metrics-based features for improving typography on the web: | ||
### createFontStack | ||
Creates metrics-based `@font-face` declarations to improve the alignment of font family fallbacks, which can dramatically improve the [Cumulative Layout Shift](https://web.dev/cls/) metric for sites that depend on a web font. | ||
#### Usage | ||
Consider the following example, where the desired web font is [Lobster](https://fonts.google.com/specimen/Lobster), falling back to `Helvetica Neue` and then `Arial`, e.g. `font-family: Lobster, 'Helvetica Neue', Arial`. | ||
1. Import `createFontStack` from the core package: | ||
```ts | ||
import { createFontStack } from '@capsizecss/core'; | ||
``` | ||
2. Import the font metrics for each of the desired fonts (see [Font Metrics](#font-metrics) above): | ||
```ts | ||
import lobster from '@capsizecss/metrics/lobster'; | ||
import helveticaNeue from '@capsizecss/metrics/helveticaNeue'; | ||
import arial from '@capsizecss/metrics/arial'; | ||
``` | ||
3. Create your font stack passing the metrics as an array, using the same order as you would via the `font-family` CSS property. | ||
```ts | ||
const { fontFamily, fontFaces } = createFontStack([ | ||
lobster, | ||
helveticaNeue, | ||
arial, | ||
]); | ||
``` | ||
The returned value contains the generated font face declarations as well as the computed `fontFamily` with the appropriately ordered font aliases. | ||
#### Usage in CSS stylesheet or a style tag | ||
The returned values can be templated into a stylesheet or a `style` block. Here is an example [handlebars](https://handlebarsjs.com/) template: | ||
```html | ||
<style type="text/css"> | ||
.heading { | ||
font-family: {{ fontFamily }} | ||
} | ||
{{ fontFaces }} | ||
</style> | ||
``` | ||
This will produce the following CSS: | ||
```css | ||
.heading { | ||
font-family: Lobster, 'Lobster Fallback: Helvetica Neue', | ||
'Lobster Fallback: Arial'; | ||
} | ||
@font-face { | ||
font-family: 'Lobster Fallback: Helvetica Neue'; | ||
src: local('Helvetica Neue'); | ||
ascent-override: 115.1741%; | ||
descent-override: 28.7935%; | ||
size-adjust: 86.8251%; | ||
} | ||
@font-face { | ||
font-family: 'Lobster Fallback: Arial'; | ||
src: local('Arial'); | ||
ascent-override: 113.5679%; | ||
descent-override: 28.392%; | ||
size-adjust: 88.053%; | ||
} | ||
``` | ||
#### Usage with CSS-in-JS frameworks | ||
If working with a CSS-in-JS library, the returned `fontFaces` can be provided as a JavaScript style object by providing `styleObject` as a `fontFaceFormat` option. | ||
Here is an example using [Emotion](https://emotion.sh/): | ||
```tsx | ||
import { Global } from '@emotion/core'; | ||
const { fontFaces, fontFamily } = createFontStack( | ||
[lobster, helveticaNeue, arial], | ||
{ | ||
fontFaceFormat: 'styleObject', | ||
}, | ||
); | ||
export const App = () => ( | ||
<> | ||
<Global styles={fontFaces} /> | ||
<p css={{ fontFamily }}>...</p> | ||
</> | ||
); | ||
``` | ||
> Also useful as a source for further manipulation given it is a data structure that can be iterated over or extended. | ||
#### Providing additional `font-face` properties | ||
Additional properties can be added to the generated `@font-face` declarations via the `fontFaceProperties` option: | ||
```ts | ||
const { fontFamily, fontFaces } = createFontStack( | ||
[lobster, helveticaNeue, arial], | ||
{ | ||
fontFaceProperties: { | ||
fontDisplay: 'swap', | ||
}, | ||
}, | ||
); | ||
``` | ||
This will result in the following additions to the declarations: | ||
```diff | ||
@font-face { | ||
font-family: 'Lobster Fallback: Helvetica Neue'; | ||
src: local('Helvetica Neue'); | ||
ascent-override: 115.1741%; | ||
descent-override: 28.7935%; | ||
size-adjust: 86.8251%; | ||
+ font-display: swap; | ||
} | ||
@font-face { | ||
font-family: 'Lobster Fallback: Arial'; | ||
src: local('Arial'); | ||
ascent-override: 113.5679%; | ||
descent-override: 28.392%; | ||
size-adjust: 88.053%; | ||
+ font-display: swap; | ||
} | ||
``` | ||
Worth noting, passing any of the metric override CSS properties will be ignored as they are calculated by Capsize. However, the `size-adjust` property is accepted to support fine-tuning the override for particular use cases. This can be used to finesse the adjustment for specific text, or to disable the adjustment by setting it to `100%`. | ||
### precomputeValues | ||
Returns all the information required to create styles for a specific font size given the provided font metrics. This is useful for integrations with different styling solutions. | ||
Returns all the information required to create leading trim styles for a specific font size given the provided font metrics. This is useful for integrations with different styling solutions. | ||
Accepts the same [options](#options) as [createStyleObject](#createstyleobject) and [createStyleString](#createstylestring). | ||
```ts | ||
import { precomputeValues } from '@capsizecss/core'; | ||
import arialMetrics from '@capsizecss/metrics/arial'; | ||
const capsizeValues = precomputeValues({ | ||
fontSize: 24, | ||
fontMetrics: { | ||
... | ||
} | ||
}) | ||
fontSize: 16, | ||
leading: 24, | ||
fontMetrics: arialMetrics, | ||
}); | ||
@@ -192,9 +347,8 @@ // => { | ||
import { getCapHeight } from '@capsizecss/core'; | ||
import arialMetrics from '@capsizecss/metrics/arial'; | ||
const actualCapHeight = getCapHeight({ | ||
fontSize: 24, | ||
fontMetrics: { | ||
... | ||
} | ||
}) | ||
fontMetrics: arialMetrics, | ||
}); | ||
@@ -204,5 +358,22 @@ // => number | ||
<br /> | ||
<br /> | ||
## Metrics | ||
To make the retrieval of font metrics easy, Capsize provides the `@capsizecss/metrics` package containing all the required data for both system and Google fonts. | ||
```bash | ||
npm install @capsizecss/metrics | ||
``` | ||
See the [package](packages/metrics/README.md) for documentation. | ||
## Unpack | ||
If you are using a custom font or one not included in the `@capsizecss/metrics` package, Capsize provides the `@capsizecss/unpack` package to extract the required data either via a URL or from a local file. | ||
```bash | ||
npm install @capsizecss/unpack | ||
``` | ||
See the [package](packages/unpack/README.md) for documentation. | ||
## Integrations | ||
@@ -212,5 +383,2 @@ | ||
<br /> | ||
<br /> | ||
## Thanks | ||
@@ -222,7 +390,4 @@ | ||
<br /> | ||
<br /> | ||
## License | ||
MIT. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
86444
19
1925
383
1
2
1
+ Addedcsstype@^3.1.1
+ Addedcsstype@3.1.3(transitive)