{"id":13725166,"url":"https://github.com/kyleshevlin/shevyjs","last_synced_at":"2025-04-07T10:28:06.570Z","repository":{"id":20993672,"uuid":"89897151","full_name":"kyleshevlin/shevyjs","owner":"kyleshevlin","description":"Configurable Vertical Rhythm \u0026 Typography in CSS-in-JS","archived":false,"fork":false,"pushed_at":"2023-01-08T01:28:02.000Z","size":1863,"stargazers_count":318,"open_issues_count":11,"forks_count":9,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-04-14T21:54:45.176Z","etag":null,"topics":["css-in-js","javascript","react","typography","vertical-rhythm"],"latest_commit_sha":null,"homepage":"","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/kyleshevlin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-05-01T04:26:36.000Z","updated_at":"2024-03-01T15:32:47.000Z","dependencies_parsed_at":"2023-01-12T03:31:08.814Z","dependency_job_id":null,"html_url":"https://github.com/kyleshevlin/shevyjs","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kyleshevlin%2Fshevyjs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kyleshevlin%2Fshevyjs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kyleshevlin%2Fshevyjs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kyleshevlin%2Fshevyjs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kyleshevlin","download_url":"https://codeload.github.com/kyleshevlin/shevyjs/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247634847,"owners_count":20970615,"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":["css-in-js","javascript","react","typography","vertical-rhythm"],"created_at":"2024-08-03T01:02:14.767Z","updated_at":"2025-04-07T10:28:06.548Z","avatar_url":"https://github.com/kyleshevlin.png","language":"TypeScript","funding_links":[],"categories":["TypeScript","JavaScript","Libraries"],"sub_categories":[],"readme":"# ShevyJS\n\n\u003e Perfect vertical rhythm for typography and more in CSS-in-JS\n\nShevyJS takes the concepts of the original [Shevy](https://github.com/kyleshevlin/shevy) and makes them available for CSS-in-JS systems. Shevy will do all the math required to keep your typography (and more) on your design system's baseline.\n\n- [ShevyJS](#shevyjs)\n  - [Installation](#installation)\n    - [Warning](#warning)\n  - [Usage](#usage)\n  - [API](#api)\n    - [`createShevy`](#createshevy)\n  - [Default Options](#default-options)\n    - [baseFontSize](#basefontsize)\n    - [baseLineHeight](#baselineheight)\n    - [fontScale](#fontscale)\n    - [includeMarginBottom](#includemarginbottom)\n    - [proximity](#proximity)\n    - [precision](#precision)\n  - [Properties](#properties)\n  - [Methods](#methods)\n    - [lineHeightSpacing](#lineheightspacing)\n    - [baseSpacing](#basespacing)\n  - [Upgrade path from v1 to v2](#upgrade-path-from-v1-to-v2)\n  - [Example Uses of Shevy Methods](#example-uses-of-shevy-methods)\n    - [As Inline Styles](#as-inline-styles)\n    - [With Styled Components](#with-styled-components)\n    - [With Emotion](#with-emotion)\n    - [Recipes](#recipes)\n\n## Installation\n\nShevy is available as a module from npm:\n\n```\nnpm install shevyjs\n```\n\nOr, with Yarn:\n\n```\nyarn add shevyjs\n```\n\n### Warning\n\nBe sure that you get the `shevyjs` module, otherwise you might accidentally import the Sass version of this library instead.\n\n## Usage\n\nShevyJS is not designed for any particular framework or CSS-in-JS solution. It can be used anywhere you can use JavaScript. The following example will use React, but you should be able to apply these concepts to your needs with ease.\n\n```jsx\nimport React from 'react'\nimport createShevy from 'shevyjs' // const Shevy = require('shevyjs').default if using CommonJS\n\nconst shevy = createShevy() // factory function for creating Shevy objects\nconst { h1, content } = shevy // Destructures the styles for h1 and content-based tags\n\nconst MyComponent = () =\u003e (\n  \u003cdiv\u003e\n    \u003ch1 style={h1}\u003eShevyJS\u003c/h1\u003e\n    \u003cp style={content}\u003eShevy's not just for Sass anymore.\u003c/p\u003e\n  \u003c/div\u003e\n)\n\nexport default MyComponent\n```\n\nCreating a Shevy object will generate a set of properties that you can use for your styles in your components. Typically, you'll create a single Shevy object and utilize that throughout your application, but it's certainly possible to make localized `shevy`s for your use cases.\n\n## API\n\n### `createShevy`\n\n`createShevy: (options?: ShevyOptions = {}) =\u003e Shevy`\n\n`createShevy` is a factory function for creating Shevy objects. Invoking it without `options` will utilize the `defaultOptions` discussed below. `createShevy` can receive an object of `ShevyOptions` to be merged with the defaults.\n\nThe most common use would be something like the following:\n\n```javascript\n// shevy.js\nimport createShevy from 'shevyjs'\n\nconst shevy = createShevy()\nexport default shevy\n\n// I like to alias the baseSpacing function to a shorthand for my projects\n// since it is commonly used for paddings and margins\nexport const bs = shevy.baseSpacing\n```\n\n## Default Options\n\nShevyJS comes with a set of defaults that can be easily overwritten. To overwrite any of these options, pass an options object into `Shevy()` at instantiation. Your options object will be merged with the default options, so you can declare as many or as few of the options as you would like.\n\nHere are the defaults:\n\n```javascript\nconst defaultOptions = {\n  baseFontSize: '16px',\n  baseLineHeight: 1.5,\n  fontScale: [3, 2.5, 2, 1.5, 1.25, 1],\n  includeMarginBottom: true,\n  proximity: null,\n  precision: null,\n}\n```\n\nBelow is a description of what each option does for ShevyJS.\n\n### baseFontSize\n\n`baseFontSize: string`\n\nThis is the size you want to base your typography on. Typically, this will be the smallest or default size of your typography. It is possible to use `fontScale`s in a way that would allow for headings to be smaller than your default font size, but it's unlikely you'll use ShevyJS this way.\n\n### baseLineHeight\n\n`baseLineHeight: number`\n\nThis is used to determine the line height calculations in Shevy. _It is required that this value be unitless and a number_.\n\nLine heights for your headings will be based on multiples of `baseLineHeight / 2`. Half increments of your `baseLineHeight`, while strictly speaking will shift your rhythm, are typically more aesthetically pleasing that strictly using whole increments. This prevents `fontSizes` slightly larger than a `baseLineHeight` multiple from becoming excessively large.\n\nExample: a `fontSize` of `50px` and a `baseLineHeight` of `48px` doesn't result in a `96px` `lineHeight`, but rather a `72px` `lineHeight` (`48/2` gives us a half increment of `24`. `24*3` is the first size greater than `50`, thus `72px`).\n\n### fontScale\n\n`fontScale: number[]`\n\nThis is an array, of max length 6 (any values beyond the 6th will be trimmed), that is used to generate the `h1` through `h6` styles. Each value should be a number, that will be multiplied by the `baseFontSize` to generate the font size for that heading.\n\nFont scale presets are available based on [ModularScale.com](http://www.modularscale.com/). If `baseFontScale` is a string, it will attempt to match one of the following presets by key. **If a match does not exist, an error will be thrown.** The presets are:\n\n```javascript\n{\n  majorSecond: [1.802, 1.602, 1.424, 1.266, 1.125, 1],\n  minorThird: [2.488, 2.074, 1.728, 1.44, 1.2, 1],\n  majorThird: [3.052, 2.441, 1.953, 1.563, 1.25, 1],\n  perfectFourth: [4.209, 3.157, 2.369, 1.777, 1.333, 1],\n  augmentedFourth: [5.653, 3.998, 2.827, 1.999, 1.414, 1]\n}\n```\n\nYou may supply fewer than 6 values to the `fontScale`. Doing so will result in the corresponding `h*` values return objects that look like:\n\n```javascript\n{\n  fontSize: undefined,\n  lineHeight: undefined,\n  marginBottom: undefined,\n}\n```\n\nThis API was chosen to ensure type-safety and convenience. `h1` through `h6` will always exist on a `shevy` object. You will not need conditionals to check for their existence. But perhaps your application never uses `h4` through `h6`, and thus it's needless to define them. This gives you that flexibility.\n\n### includeMarginBottom\n\n`includeMarginBottom: boolean`\n\nThis determines whether a value will be set for the `marginBottom` of your style objects.\n\n### proximity\n\n`proximity: null | number`\n\nIt is often more aesthetically pleasing to make your margins smaller than your baseline would typically warrant. This is due to the fact that line height in CSS is applied above and below the `fontSize`. This results in half the extra line height sitting below the text. Adding spacing beyond this, while mathematically correct, may not be the look you are going for.\n\nBy default, `proximity` is set to null, but setting it to a `number` will tweak the spacings by that percentage.\n\n### precision\n\n`precision: null | number`\n\nBy default, `precision` is set to `null`, but setting it to a number will result in values that do not exceed that number of places after the decimal. Example: `1.23456` with a precision of `4` becomes `1.2346`.\n\n## Properties\n\nEach Shevy object comes with a set of properties to use for your styles. Each property is a JavaScript object of the following styles:\n\n```javascript\n{\n  fontSize: string\n  lineHeight: number | string\n  marginBottom: string\n}\n```\n\nThere are cases (see [fontScale](#fontscale)) where these properties are `undefined`.\n\nHere are the available properties:\n\n- `h1`, `h2`, `h3`, `h4`, `h5`, `h6`\n- `body`\n- `content`\n\n`h1` through `h6` properties map to the results of calculating your options. Here is an example of one of these objects:\n\n```javascript\nconst shevy = createShevy()\n\nconsole.log(shevy.h1) // { fontSize: '48px', lineHeight: 1, marginBottom: '24px' }\n```\n\nThe `body` property is intended to go on the `\u003cbody\u003e` tag selector and is ported over from the original Shevy. This may be less necessary in a component based JS system and might be deprecated in the future. Here is an example of the `body` object:\n\n```javascript\nconst shevy = createShevy()\n\nconsole.log(shevy.body) // { fontSize: '16px', lineHeight: 1.5 }\n```\n\nThe `content` tag is intended to be used for any base content level components. In the original Shevy, this was a mixin that directly applied styles to the `\u003cp\u003e`, `\u003col\u003e`, `\u003cul\u003e`, and `\u003cpre\u003e` tags. Now in ShevyJS, you have much more freedom to apply these styles to whatever component you deem fit. Here is an example of the `content` object:\n\n```javascript\nconst shevy = createShevy()\n\nconsole.log(shevy.content) // { fontSize: '16px', lineHeight: 1.5, marginBottom: '24px' }\n```\n\n## Methods\n\nShevy has two methods that can be useful in your design system for creating distances that fall in line with your baseline grid.\n\n### lineHeightSpacing\n\n`lineHeightSpacing: (factor?: number = 1) =\u003e string`\n\nThe `lineHeightSpacing()` method takes one argument, a number (which defaults to 1), and multiplies it with the result of the `baseFontSize` multiplied by the `baseLineHeight`.\n\n### baseSpacing\n\n`baseSpacing: (factor?: number = 1) =\u003e string`\n\nThe `baseSpacing()` method takes one argument, a number (which defaults to 1), and multiplies it with the result of `baseFontSize` multiplied by the `baseLineHeight`. It is additionally multiplies by `proximity` if it is not `null`.\n\n## Upgrade path from v1 to v2\n\nThere are a few differences between v1 to v2, so here's how to make those changes.\n\n- The default import is no longer a `Shevy` class constructor. It is the `createShevy` factory function.\n\n```diff\n- import Shevy from 'shevyjs'\n+ import createShevy from 'shevyjs'\n```\n\n- Replace instances of `new Shevy(options)` with `createShevy(options)`\n\n```diff\n- const shevy = new Shevy()\n+ const shevy = createShevy()\n```\n\n- Some `options` properties were renamed:\n\n  - `baseFontScale` is now just `fontScale`\n  - `addMarginBottom` is now `includeMarginBottom`\n\n- Some `options` properties were modified\n\n  - `precision` and `usePrecision` are now just `precision`. `precision` is a nullable property now, replacing the need for two options.\n  - `proximity` and `proximityFactor` are now just `proximity`. `proximity` is a nullable property now, replacing the need for two options.\n\n```diff\nconst options = {\n-  precision: 4,\n-  usePrecision: true,\n-  proximity: true,\n-  proximityFactor: 0.85\n+  precision: 4,\n+  proximity: 0.85,\n}\n```\n\n- Several properties are no longer accessible on the `shevy` object\n\nIn v1, `shevy` was an instance of the `Shevy` class. Because of this, certain values were made properties of the class that really didn't need to be. An example would be `this.baseFontScale`. There isn't a good reason for this to need to be on the `Shevy` class.\n\nWith the conversion to a simple factory function, it was easy to keep certain values and functions private. This tidies up the exposed API to just the properties for styles, and the methods listed above.\n\nThis also improves the types for the project, which will likely improve your editor experience with faster Intellisense for `shevy` objects.\n\n## Example Uses of Shevy Methods\n\n### As Inline Styles\n\n```jsx\nimport React from 'react'\nimport createShevy from 'shevyjs'\n\nconst shevy = createShevy()\nconst { lineHeightSpacing: lhs, baseSpacing: bs } = shevy // Destructure and alias methods\n\nconst wrap = {\n  marginBottom: lhs(2),\n}\n\nconst box = {\n  padding: bs(0.5),\n  marginBottom: bs(),\n}\n\nconst MyComponent = () =\u003e (\n  \u003cdiv style={wrap}\u003e\n    \u003cdiv style={box}\u003eBox 1\u003c/div\u003e\n    \u003cdiv style={box}\u003eBox 2\u003c/div\u003e\n  \u003c/div\u003e\n)\n```\n\n### With Styled Components\n\n```jsx\nimport React from 'react'\nimport styled from 'styled-components'\nimport createShevy from 'shevyjs'\n\nconst shevy = createShevy()\nconst {\n  baseSpacing: bs,\n  h1: { fontSize, lineHeight, marginBottom },\n} = shevy\n\nconst Wrap = styled.div`\n  padding: ${bs()};\n  margin-bottom: ${bs(2)};\n`\n\nconst Heading = styled.h1`\n  font-size: ${fontSize};\n  line-height: ${lineHeight};\n  margin-bottom: ${marginBottom};\n`\n\nconst MyComponent = () =\u003e (\n  \u003cWrap\u003e\n    \u003cHeading\u003eShevy with Styled Components!\u003c/Heading\u003e\n  \u003c/Wrap\u003e\n)\n```\n\n### With Emotion\n\n```javascript\nimport createShevy from 'shevyjs'\nimport { css } from 'emotion'\n\nconst shevy = createShevy()\nconst { content } = shevy\nconst app = document.getElementById('root')\nconst myStyle = css`\n  color: rebeccapurple;\n  font-size: ${content.fontSize};\n`\napp.classList.add(myStyle)\n```\n\nAnd Emotion with React:\n\n```jsx\nimport React from 'react'\nimport styled, { css } from 'emotion'\nimport Shevy from 'shevyjs'\n\nconst shevy = createShevy()\nconst {\n  baseSpacing: bs,\n  h1: { fontSize, lineHeight, marginBottom },\n} = shevy\n\nconst Wrap = styled('div')`\n  padding: ${bs()};\n  margin-bottom: ${bs(2)};\n`\n\nconst Heading = styled('h1')`\n  font-size: ${fontSize};\n  line-height: ${lineHeight};\n  margin-bottom: ${marginBottom};\n`\n\nconst MyComponent = () =\u003e (\n  \u003cWrap\u003e\n    \u003cHeading\u003eShevy with Emotion and React!\u003c/Heading\u003e\n  \u003c/Wrap\u003e\n)\n```\n\n### Recipes\n\nCreate a `Spacer` component to use with `shevy` (inspired by [this Max Stoiber article](https://mxstbr.com/thoughts/margin)):\n\n```jsx\nimport React from 'react'\nimport createShevy from 'shevyjs'\n\nconst shevy = createShevy()\nconst bs = shevy.baseSpacing\n\nfunction Spacer({\n  children,\n  all = 0,\n  horz = 0,\n  vert = 0,\n  top = 0,\n  right = 0,\n  bottom = 0,\n  left = 0,\n}) {\n  const margins = {\n    ...(all \u0026\u0026 { margin: bs(all) }),\n    ...(horz \u0026\u0026 { marginLeft: bs(horz), marginRight: bs(horz) }),\n    ...(vert \u0026\u0026 { marginTop: bs(vert), marginBottom: bs(vert) }),\n    ...(top \u0026\u0026 { marginTop: bs(top) }),\n    ...(right \u0026\u0026 { marginRight: bs(right) }),\n    ...(bottom \u0026\u0026 { marginBottom: bs(bottom) }),\n    ...(left \u0026\u0026 { marginLeft: bs(left) }),\n  }\n\n  return \u003cdiv style={margins}\u003e{children}\u003c/div\u003e\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkyleshevlin%2Fshevyjs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkyleshevlin%2Fshevyjs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkyleshevlin%2Fshevyjs/lists"}