{"id":13613394,"url":"https://github.com/seek-oss/capsize","last_synced_at":"2025-05-13T16:11:12.490Z","repository":{"id":38432681,"uuid":"269211843","full_name":"seek-oss/capsize","owner":"seek-oss","description":"Flipping how we define typography in CSS.","archived":false,"fork":false,"pushed_at":"2025-03-31T00:25:22.000Z","size":44268,"stargazers_count":1606,"open_issues_count":9,"forks_count":42,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-05-11T18:08:51.005Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://seek-oss.github.io/capsize/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/seek-oss.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-06-03T22:57:36.000Z","updated_at":"2025-05-08T10:16:02.000Z","dependencies_parsed_at":"2023-01-24T15:45:28.755Z","dependency_job_id":"39ba6244-7482-4138-bf8b-47a86feee9c2","html_url":"https://github.com/seek-oss/capsize","commit_stats":{"total_commits":166,"total_committers":13,"mean_commits":12.76923076923077,"dds":0.4036144578313253,"last_synced_commit":"42d6dc39d58247bc6b9e013a4b1c4463bf287dca"},"previous_names":[],"tags_count":43,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seek-oss%2Fcapsize","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seek-oss%2Fcapsize/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seek-oss%2Fcapsize/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seek-oss%2Fcapsize/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/seek-oss","download_url":"https://codeload.github.com/seek-oss/capsize/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253609637,"owners_count":21935560,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T20:00:45.780Z","updated_at":"2025-05-13T16:11:07.475Z","avatar_url":"https://github.com/seek-oss.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"\u003cimg src=\"https://raw.githubusercontent.com/seek-oss/capsize/HEAD/images/capsize-header.png#gh-light-mode-only\" alt=\"Capsize\" title=\"Capsize\" width=\"443px\" /\u003e\n\u003cimg src=\"https://raw.githubusercontent.com/seek-oss/capsize/HEAD/images/capsize-header-inverted.png#gh-dark-mode-only\" alt=\"Capsize\" title=\"Capsize\" width=\"443px\" /\u003e\n\u003cbr/\u003e\n\n\u003e Capsize makes the sizing and layout of text as predictable as every other element on the screen.\n\u003e\n\u003e Using font metadata, text can now be sized according to the height of its capital letters while trimming the space above capital letters and below the baseline.\n\n```bash\nnpm install @capsizecss/core\n```\n\n- [Usage](#usage)\n  - [createStyleObject](#createstyleobject)\n  - [createStyleString](#createstylestring)\n- [Options](#options)\n  - [Text size](#text-size)\n  - [Line height](#line-height)\n  - [Font Metrics](#font-metrics)\n- [Core](#core)\n  - [createFontStack](#createfontstack)\n    - [Usage in CSS stylesheet](#usage-in-css-stylesheet-or-a-style-tag)\n    - [Usage with CSS-in-JS frameworks](#usage-with-css-in-js-frameworks)\n    - [Additional `font-face` properties](#providing-additional-font-face-properties)\n  - [precomputeValues](#precomputevalues)\n  - [getCapHeight](#getcapheight)\n- [Metrics](#metrics)\n- [Unpack](#unpack)\n- [Integrations](#integrations)\n  - [vanilla-extract](packages/vanilla-extract/README.md)\n\n## Usage\n\n### `createStyleObject`\n\nReturns a CSS-in-JS style object.\n\n1. Import `createStyleObject` passing the relevant [options](#options).\n\n```ts\nimport { createStyleObject } from '@capsizecss/core';\n\nconst capsizeStyles = createStyleObject({\n  fontSize: 16,\n  leading: 24,\n  fontMetrics: {\n    capHeight: 700,\n    ascent: 1058,\n    descent: -291,\n    lineGap: 0,\n    unitsPerEm: 1000,\n  },\n});\n```\n\nNote: It is recommended that you install the [@capsizecss/metrics](packages/metrics/README.md) package and import the metrics from there:\n\n```ts\nimport { createStyleObject } from '@capsizecss/core';\nimport arialMetrics from '@capsizecss/metrics/arial';\n\nconst capsizeStyles = createStyleObject({\n  fontSize: 16,\n  leading: 24,\n  fontMetrics: arialMetrics,\n});\n```\n\nSee the [fontMetrics](#font-metrics) option documented below for more ways to obtain these metrics.\n\n2. Apply styles to the text element, for example via the `css` prop.\n\n```ts\n\u003cdiv\n  css={{\n    // fontFamily: '...' etc,\n    ...capsizeStyles,\n  }}\n\u003e\n  My capsized text 🛶\n\u003c/div\u003e\n```\n\n\u003e ⚠️ Note: It is not recommended to apply further layout-related styles to the same element, as this will risk interfering with the styles used for the trim. Instead consider using a nested element.\n\n### `createStyleString`\n\nReturns a CSS string that can be inserted into a `style` tag or appended to a stylesheet.\n\n1. Import `createStyleString` passing the relevant [options](#options).\n\n```ts\nimport { createStyleString } from '@capsizecss/core';\nimport arialMetrics from '@capsizecss/metrics/arial';\n\nconst capsizedStyleRule = createStyleString('capsizedText', {\n  fontSize: 16,\n  leading: 24,\n  fontMetrics: arialMetrics,\n});\n```\n\n2. Add the styles into a stylesheet or `style` element and apply the specified class name.\n\n```ts\ndocument.write(`\n  \u003cstyle type=\"text/css\"\u003e\n    ${capsizedStyleRule}\n  \u003c/style\u003e\n  \u003cdiv class=\"capsizedText\"\u003e\n    My capsized text 🛶\n  \u003c/div\u003e\n`);\n```\n\n\u003e ⚠️ Note: It is not recommended to apply further layout-related styles to the same element, as this will risk interfering with the styles used for the trim. Instead consider using a nested element.\n\n## Options\n\n### Text size\n\nCapsize supports two methods of defining the size of text, `capHeight` and `fontSize`.\n\n**NOTE: You should only ever pass one or the other, not both.**\n\n#### `capHeight: \u003cnumber\u003e`\n\nSets the height of the capital letters to the defined value. Defining typography in this way makes aligning to a grid or with other elements, e.g. icons, a breeze.\n\n\u003cimg src=\"https://raw.githubusercontent.com/seek-oss/capsize/HEAD/images/capheight.png\" alt=\"Highlighting the cap height\" title=\"Cap Height\" width=\"220px\" /\u003e\n\n#### `fontSize: \u003cnumber\u003e`\n\nSetting the font size allows you to get all the benefits of the white space trimming, while still specifying an explicit `font-size` for your text. This can be useful when needed to match a concrete design spec or fitting into an existing product.\n\n\u003cimg src=\"https://raw.githubusercontent.com/seek-oss/capsize/HEAD/images/fontsize.png\" alt=\"Highlighting the font size\" title=\"Font Size\" width=\"220px\" /\u003e\n\n### Line height\n\nCapsize supports two mental models for specifying line height, `lineGap` and `leading`. If you pass neither the text will follow the default spacing of the specified font, e.g. `line-height: normal`.\n\n**NOTE: You should only ever pass one or the other, not both.**\n\n#### `lineGap: \u003cnumber\u003e`\n\nSets the number of pixels between lines, as measured between the baseline and cap height of the next line.\n\n\u003cimg src=\"https://raw.githubusercontent.com/seek-oss/capsize/HEAD/images/linegap.png\" alt=\"Highlighting the line gap\" title=\"Line Gap\" width=\"220px\" /\u003e\n\n#### `leading: \u003cnumber\u003e`\n\nSets the line height to the provided value as measured from the baseline of the text. This aligns the web with how typography is treated in design tools.\n\n\u003cimg src=\"https://raw.githubusercontent.com/seek-oss/capsize/HEAD/images/leading.png\" alt=\"Highlighting the leading\" title=\"Leading\" width=\"220px\" /\u003e\n\n### Font Metrics\n\nThis metadata is extracted from the metrics tables inside the font itself. There are a number of ways to find this information:\n\n- 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:\n  ```ts\n  import arialMetrics from '@capsizecss/metrics/arial';\n  ```\n- 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:\n\n  ```ts\n  import { fromFile } from '@capsizecss/unpack';\n\n  const metrics = await fromFile(filePath);\n  ```\n\n- 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.\n\n## Core\n\nThe core package also provides a few other metrics-based features for improving typography on the web:\n\n### createFontStack\n\nCreates 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.\n\n#### Usage\n\nConsider 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`.\n\n1. Import `createFontStack` from the core package:\n\n```ts\nimport { createFontStack } from '@capsizecss/core';\n```\n\n2. Import the font metrics for each of the desired fonts (see [Font Metrics](#font-metrics) above):\n\n```ts\nimport lobster from '@capsizecss/metrics/lobster';\nimport helveticaNeue from '@capsizecss/metrics/helveticaNeue';\nimport arial from '@capsizecss/metrics/arial';\n```\n\n3. Create your font stack passing the metrics as an array, using the same order as you would via the `font-family` CSS property.\n\n```ts\nconst { fontFamily, fontFaces } = createFontStack([\n  lobster,\n  helveticaNeue,\n  arial,\n]);\n```\n\nThe returned value contains the generated font face declarations as well as the computed `fontFamily` with the appropriately ordered font aliases.\n\n#### Usage in CSS stylesheet or a style tag\n\nThe returned values can be templated into a stylesheet or a `style` block. Here is an example [handlebars](https://handlebarsjs.com/) template:\n\n```html\n\u003cstyle type=\"text/css\"\u003e\n  .heading {\n    font-family: {{ fontFamily }}\n  }\n\n  {{ fontFaces }}\n\u003c/style\u003e\n```\n\nThis will produce the following CSS:\n\n```css\n.heading {\n  font-family: Lobster, 'Lobster Fallback: Helvetica Neue',\n    'Lobster Fallback: Arial', 'Helvetica Neue', Arial;\n}\n\n@font-face {\n  font-family: 'Lobster Fallback: Helvetica Neue';\n  src: local('Helvetica Neue');\n  ascent-override: 115.1741%;\n  descent-override: 28.7935%;\n  size-adjust: 86.8251%;\n}\n@font-face {\n  font-family: 'Lobster Fallback: Arial';\n  src: local('Arial');\n  ascent-override: 113.5679%;\n  descent-override: 28.392%;\n  size-adjust: 88.053%;\n}\n```\n\n#### Usage with CSS-in-JS frameworks\n\nIf 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.\n\nHere is an example using [Emotion](https://emotion.sh/):\n\n```tsx\nimport { Global } from '@emotion/core';\n\nconst { fontFaces, fontFamily } = createFontStack(\n  [lobster, helveticaNeue, arial],\n  {\n    fontFaceFormat: 'styleObject',\n  },\n);\n\nexport const App = () =\u003e (\n  \u003c\u003e\n    \u003cGlobal styles={fontFaces} /\u003e\n    \u003cp css={{ fontFamily }}\u003e...\u003c/p\u003e\n  \u003c/\u003e\n);\n```\n\n\u003e Also useful as a source for further manipulation given it is a data structure that can be iterated over or extended.\n\n#### Providing additional `font-face` properties\n\nAdditional properties can be added to the generated `@font-face` declarations via the `fontFaceProperties` option:\n\n```ts\nconst { fontFamily, fontFaces } = createFontStack(\n  [lobster, helveticaNeue, arial],\n  {\n    fontFaceProperties: {\n      fontDisplay: 'swap',\n    },\n  },\n);\n```\n\nThis will result in the following additions to the declarations:\n\n```diff\n @font-face {\n   font-family: 'Lobster Fallback: Helvetica Neue';\n   src: local('Helvetica Neue');\n   ascent-override: 115.1741%;\n   descent-override: 28.7935%;\n   size-adjust: 86.8251%;\n+  font-display: swap;\n }\n @font-face {\n   font-family: 'Lobster Fallback: Arial';\n   src: local('Arial');\n   ascent-override: 113.5679%;\n   descent-override: 28.392%;\n   size-adjust: 88.053%;\n+  font-display: swap;\n }\n```\n\n\u003e [!NOTE]\n\u003e Passing any of the metric override CSS properties will be ignored as they are calculated by Capsize.\n\u003e However, the `size-adjust` property is accepted to support fine-tuning the override for particular use cases.\n\u003e This can be used to finesse the adjustment for specific text, or to disable the adjustment by setting it to `100%`.\n\n#### Scaling for different character subsets\n\nFor languages that use different unicode subsets, e.g. Thai, the fallbacks need to be scaled accordingly, as the scaling is [based on character frequency in written language].\n\nA fallback font stack can be generated for a supported subset by specifying `subset` as an option:\n\n```ts\nconst { fontFamily, fontFaces } = createFontStack([lobster, arial], {\n  subset: 'thai',\n});\n```\n\n\u003e [!TIP]\n\u003e Need support for a different unicode subset?\n\u003e Either create an issue or follow the steps outlined in the [`generate-weightings` script] and open a PR.\n\n[based on character frequency in written language]: packages/metrics/README.md#how-xwidthavg-is-calculated\n[`generate-weightings` script]: packages/unpack/scripts/generate-weightings.ts\n\n### precomputeValues\n\nReturns 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.\n\nAccepts the same [options](#options) as [createStyleObject](#createstyleobject) and [createStyleString](#createstylestring).\n\n```ts\nimport { precomputeValues } from '@capsizecss/core';\nimport arialMetrics from '@capsizecss/metrics/arial';\n\nconst capsizeValues = precomputeValues({\n  fontSize: 16,\n  leading: 24,\n  fontMetrics: arialMetrics,\n});\n\n// =\u003e {\n//  fontSize: string,\n//  lineHeight: string,\n//  capHeightTrim: string,\n//  baselineTrim: string,\n//}\n```\n\n### getCapHeight\n\nReturn the rendered cap height for a specific font size given the provided font metrics.\n\n```ts\nimport { getCapHeight } from '@capsizecss/core';\nimport arialMetrics from '@capsizecss/metrics/arial';\n\nconst actualCapHeight = getCapHeight({\n  fontSize: 24,\n  fontMetrics: arialMetrics,\n});\n\n// =\u003e number\n```\n\n## Metrics\n\nTo make the retrieval of font metrics easy, Capsize provides the `@capsizecss/metrics` package containing all the required data for both system and Google fonts.\n\n```bash\nnpm install @capsizecss/metrics\n```\n\nSee the [package](packages/metrics/README.md) for documentation.\n\n## Unpack\n\nIf 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.\n\n```bash\nnpm install @capsizecss/unpack\n```\n\nSee the [package](packages/unpack/README.md) for documentation.\n\n## Integrations\n\n- [vanilla-extract](https://vanilla-extract.style) integration via [@capsizecss/vanilla-extract](packages/vanilla-extract/README.md)\n\n## Thanks\n\n- [Vincent De Oliveira](https://twitter.com/iamvdo) for writing [Deep dive CSS: font metrics, line-height and vertical-align](https://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align), which provided the research needed to build all this.\n- [Devon Govett](https://github.com/devongovett) for creating [Fontkit](https://github.com/foliojs/fontkit), which does all the heavy lifting of extracting the font metrics under the covers.\n- [SEEK](https://www.seek.com.au) for giving us the space to do interesting work.\n\n## License\n\nMIT.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseek-oss%2Fcapsize","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseek-oss%2Fcapsize","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseek-oss%2Fcapsize/lists"}