UNPKG

@lyubomir-popov/baseline-nudge-generator

Version:

Automatic font metrics reader that generates baseline grid nudges for CSS typography systems

528 lines (413 loc) โ€ข 16.8 kB
# @lyubomir-popov/baseline-nudge-generator Automatic font metrics reader that generates baseline grid nudges for CSS typography. Extracts precise font metrics from webfont files and calculates the exact CSS nudges needed to align text to a baseline grid. ## Quick Start 1. **Install the package:** ```bash npm install -g @lyubomir-popov/baseline-nudge-generator ``` 2. **Set up your project (one command does it all):** ```bash baseline-nudges setup ``` 3. **Generate tokens and HTML:** ```bash baseline-nudges generate config/typography-config.json baseline-nudges generate config/typography-config.json custom-output ``` 4. **View the result:** ```bash open dist/index.html # Or if you specified a custom output directory: open custom-output/index.html ``` ## What's New in This Version - **๐ŸŽจ Multi-font support**: Use multiple font families (sans, serif) with different nudge calculations per element - **โš–๏ธ Per-element font styling**: Specify `fontFamily`, `fontWeight`, and `fontStyle` for each element - **๐Ÿ”ง Fixed critical baseline calculation bug**: Corrected lineGap distribution formula for accurate baseline alignment - **๐Ÿ“ Improved baseline offset calculation**: Now properly distributes lineGap above and below text baseline - **๐ŸŽจ Complete typescale support**: Full h1-h6 and paragraph element configuration - **๐Ÿงน Production-ready code**: Removed debug outputs, added ESLint, improved error handling - **๐Ÿ”ค Enhanced font name extraction**: Robust font name detection from various font formats - **๐Ÿ“ Better HTML examples**: Improved sample text format and visual presentation ## How It Works The tool uses the **corrected baseline calculation formula**: ``` baselineOffsetRem = leadingRem/2 + ascenderRem + (lineGapRem/2) ``` This ensures that lineGap is properly distributed above and below the text baseline, fixing previous calculation errors that caused misalignment. ## Configuration Format The `typography-config.json` file defines your typography scale and baseline grid settings. Two formats are supported: ### New Multi-Font Format (Recommended) ```json { "baselineUnit": 0.5, "fontFiles": [ { "family": "sans", "path": "../fonts/IBMPlexSans-Regular.woff" }, { "family": "serif", "path": "../fonts/IBMPlexSerif-Regular.woff" } ], "elements": [ { "identifier": "h1", "fontSize": 5.25, "lineHeight": 10, "spaceAfter": 3, "fontFamily": "sans", "fontWeight": 700, "fontStyle": "normal" }, { "identifier": "p", "fontSize": 1, "lineHeight": 3, "spaceAfter": 3, "fontFamily": "serif", "fontWeight": 400, "fontStyle": "normal" } ] } ``` ### Legacy Single-Font Format ```json { "baselineUnit": 0.5, "fontFile": "../fonts/FiraSans-Regular.ttf", "elements": [ { "identifier": "h1", "fontSize": 5.25, "lineHeight": 10, "spaceAfter": 3 }, { "identifier": "p", "fontSize": 1, "lineHeight": 3, "spaceAfter": 3 } ] } ``` ### Configuration Properties #### Multi-Font Format - **`baselineUnit`** (number): The baseline grid unit in rem. Common values are 0.5rem or 0.25rem. - **`fontFiles`** (array): Array of font file definitions with `family` and `path` properties. - **`elements`** (array): Array of typography elements to generate. #### Legacy Single-Font Format - **`baselineUnit`** (number): The baseline grid unit in rem. Common values are 0.5rem or 0.25rem. - **`fontFile`** (string): Relative path to your font file from the config directory. - **`elements`** (array): Array of typography elements to generate. ### Element Properties Each element in the `elements` array has these properties: #### Required Properties - **`identifier`** (string): Element identifier or CSS class name (e.g., "h1", "p", "caption", ".heading-large"). - **`fontSize`** (number): Font size in rem units (e.g., 2.5 = 2.5rem). - **`lineHeight`** (number): Number of baseline units for line height, must be a multiple of 0.25 (e.g., 5 = 5 ร— 0.5rem = 2.5rem, 5.25 = 5.25 ร— 0.5rem = 2.625rem). - **`spaceAfter`** (number): Number of baseline units for space after the element, must be 0 or a multiple of 0.25 (e.g., 0 = no space, 4 = 4 ร— 0.5rem = 2rem, 4.25 = 4.25 ร— 0.5rem = 2.125rem). **Note:** If `spaceAfter` is set to `0`, it will be treated as `0.5` baseline units for spacing purposes. The actual CSS margin-bottom will be adjusted by subtracting the baseline nudge (padding-top) to ensure that nudge + spaceAfter equals an exact multiple of the baseline unit. #### Optional Properties (Multi-Font Format Only) - **`fontFamily`** (string): Font family to use (e.g., "sans", "serif"). Defaults to "sans" if not specified. - **`fontWeight`** (number): Font weight (100-900). Defaults to 400 if not specified. - **`fontStyle`** (string): Font style ("normal" or "italic"). Defaults to "normal" if not specified. ### Important: Line Height vs Font Size **Key difference**: - `fontSize` is specified in **rem units** (e.g., 2.5 = 2.5rem) - `lineHeight` is specified as **number of baseline units** (e.g., 5 = 5 ร— 0.5rem = 2.5rem, 5.5 = 5.5 ร— 0.5rem = 2.75rem) - `spaceAfter` is specified as **number of baseline units** (e.g., 4 = 4 ร— 0.5rem = 2rem, 4.5 = 4.5 ร— 0.5rem = 2.25rem) This ensures all line heights and spacing are perfect multiples of your baseline unit (or half-baseline units for finer control), maintaining consistent vertical rhythm throughout your typography system. ### Example Calculation With `baselineUnit: 0.5`: - `fontSize: 2.5` = 2.5rem font size - `lineHeight: 5.5` = 5.5 ร— 0.5rem = 2.75rem line height - `spaceAfter: 4.5` = 4.5 ร— 0.5rem = 2.25rem space after element **Important**: The actual CSS `margin-bottom` will be calculated as `spaceAfter - nudgeTop` to ensure proper baseline alignment. For example, if the calculated nudge is 0.375rem, the margin-bottom becomes 2.25rem - 0.375rem = 1.875rem, so that the total spacing (padding-top + margin-bottom) aligns perfectly with the baseline grid. **Special case:** If `spaceAfter` is `0`, it is treated as `0.5` baseline units for spacing, so the margin-bottom is calculated as `0.5 - nudgeTop`. ## Generated Files Running `baseline-nudges generate config/typography-config.json` creates: ### `dist/tokens.json` Design tokens with calculated nudge values for each typography element: #### Multi-Font Format ```json { "baselineUnit": "0.5rem", "fontFiles": [ { "family": "sans", "path": "../fonts/IBMPlexSans-Regular.woff" }, { "family": "serif", "path": "../fonts/IBMPlexSerif-Regular.woff" } ], "elements": { "h1": { "fontSize": "5.25rem", "lineHeight": "5rem", "fontFamily": "sans", "fontWeight": 700, "fontStyle": "normal", "spaceAfter": "1.5rem", "nudgeTop": "0.48065rem" }, "p": { "fontSize": "1rem", "lineHeight": "1.5rem", "fontFamily": "serif", "fontWeight": 400, "fontStyle": "normal", "spaceAfter": "1.5rem", "nudgeTop": "0.375rem" } } } ``` #### Legacy Single-Font Format ```json { "font": "Fira Sans", "fontWeight": 400, "baselineUnit": "0.5rem", "fontFile": "../fonts/FiraSans-Regular.ttf", "elements": { "h1": { "fontSize": "5.25rem", "lineHeight": "5rem", "fontWeight": 400, "spaceAfter": "1.5rem", "nudgeTop": "0.19065rem" }, "p": { "fontSize": "1rem", "lineHeight": "1.5rem", "fontWeight": 400, "spaceAfter": "1.5rem", "nudgeTop": "0.415rem" } } } ``` ### `dist/index.html` Interactive HTML demo with: - Baseline grid overlay (red lines) - All typography elements with calculated nudges - Self-contained with embedded font ### `dist/fonts/` Copy of your font file for the HTML demo to work offline. ## Using the Generated Tokens Import the tokens into your CSS build process or design system: ### Multi-Font Format ```javascript const tokens = require("./dist/tokens.json"); // Generate CSS Object.entries(tokens.elements).forEach(([identifier, element]) => { const nudgeTopValue = parseFloat(element.nudgeTop); const spaceAfterValue = parseFloat(element.spaceAfter); const marginBottom = spaceAfterValue - nudgeTopValue; console.log(` .${identifier} { font-size: ${element.fontSize}; line-height: ${element.lineHeight}; font-family: ${element.fontFamily ? `'${element.fontFamily}', sans-serif` : 'inherit'}; font-weight: ${element.fontWeight || 400}; font-style: ${element.fontStyle || 'normal'}; padding-top: ${element.nudgeTop}; margin-bottom: ${marginBottom}rem; margin-top: 0; } `); }); ``` ### Legacy Single-Font Format ```javascript const tokens = require("./dist/tokens.json"); // Generate CSS Object.entries(tokens.elements).forEach(([identifier, element]) => { const nudgeTopValue = parseFloat(element.nudgeTop); const spaceAfterValue = parseFloat(element.spaceAfter); const marginBottom = spaceAfterValue - nudgeTopValue; console.log(` .${identifier} { font-size: ${element.fontSize}; line-height: ${element.lineHeight}; font-weight: ${element.fontWeight}; padding-top: ${element.nudgeTop}; margin-bottom: ${marginBottom}rem; margin-top: 0; } `); }); ``` Perfect for design systems that need precise typographic alignment with proper baseline grid spacing. ## Key Features - **๐ŸŽจ Multi-font support** - Use multiple font families (sans, serif) with different nudge calculations per element - **โš–๏ธ Per-element font styling** - Specify `fontFamily`, `fontWeight`, and `fontStyle` for each element - **๐Ÿ”ง Corrected baseline calculation** - Fixed critical lineGap distribution bug for accurate alignment - **๐Ÿ“ Precise font metrics reading** - Extracts exact metrics from TTF, WOFF, and OTF files - **๐ŸŽจ Complete typescale support** - Full h1-h6 and paragraph element configuration - **๐Ÿ”ค Automatic font name extraction** - Robust font name detection from various font formats - **๐ŸŽฏ Baseline nudge calculation** - Generates precise CSS nudges for baseline grid alignment - **๐Ÿ“ฑ Responsive baseline grids** - Handles tight line-heights with negative nudge compensation - **๐ŸŽญ Interactive HTML demos** - Visual examples with baseline grid overlay - **๐Ÿงน Production-ready code** - Clean, ESLint-compliant codebase without debug outputs - **๐Ÿ”„ CLI and API access** - Command-line tool and programmatic JavaScript API - **โšก One-command setup** - `baseline-nudges setup` downloads font and creates config automatically ## Installation ```bash npm install -g @lyubomir-popov/baseline-nudge-generator ``` Or for local project use: ```bash npm install --save-dev @lyubomir-popov/baseline-nudge-generator ``` ## Using Your Own Font After running `baseline-nudges setup`, you can replace the downloaded font: 1. **Replace the font file:** ```bash # Replace with your font file cp path/to/your-font.ttf fonts/FiraSans-Regular.ttf # Or rename your font and update the config cp path/to/your-font.ttf fonts/YourFont-Regular.ttf ``` 2. **Update the config file:** Edit `config/typography-config.json` and change the `fontFile` path: ```json { "baselineUnit": 0.5, "fontFile": "../fonts/YourFont-Regular.ttf", "elements": [{ "identifier": "h1", "fontSize": 2.5, "lineHeight": 5 }] } ``` 3. **Customize your typography scale:** Edit the `elements` array to match your design system: ```json { "elements": [ { "identifier": "h1", // Element identifier or CSS class name "fontSize": 3, // Font size in rem "lineHeight": 6, // Line height in baseline units (6 ร— 0.5rem = 3rem) "spaceAfter": 5 // Space after in baseline units (5 ร— 0.5rem = 2.5rem) }, { "identifier": "body-text", "fontSize": 1, "lineHeight": 3, "spaceAfter": 3 } ] } ``` 4. **Regenerate with your font:** ```bash baseline-nudges generate config/typography-config.json ``` The font name will be automatically extracted from your font file and used in the generated HTML and tokens. ## Fractional Baseline Support The baseline-nudge-generator now supports fractional line heights and spacing values in multiples of 0.25, effectively quadrupling the resolution of the baseline grid for finer typographic control. ### Fractional Values - **lineHeight**: Must be multiples of 0.25 (1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, etc.) - **spaceAfter**: Must be 0 or multiples of 0.25 (0, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, etc.). **If set to 0, it is treated as 0.5 baseline units for spacing.** ### Benefits - **Doubled Resolution**: Baseline grid effectively has 2x resolution - **More Precise Typography**: Allows for finer control over line heights and spacing - **Maintains Grid Alignment**: Still ensures proper baseline grid alignment - **Backward Compatible**: Integer values (multiples of 1.0) still work perfectly ### Example Configuration ```json { "baselineUnit": 0.5, "fontFiles": [ {"family": "sans", "path": "fonts/Inter-Regular.woff"} ], "elements": [ { "identifier": "h1", "fontSize": 2.5, "lineHeight": 5.5, // 5.5 ร— 0.5rem = 2.75rem "spaceAfter": 4.5, // 4.5 ร— 0.5rem = 2.25rem "fontFamily": "sans", "fontWeight": 700 }, { "identifier": "p", "fontSize": 1.0, "lineHeight": 2.5, // 2.5 ร— 0.5rem = 1.25rem "spaceAfter": 1.5, // 1.5 ร— 0.5rem = 0.75rem "fontFamily": "sans", "fontWeight": 400 } ] } ``` ## Font Format Support The tool supports various font formats: ### โœ… Supported Formats - **TTF (TrueType)** - Recommended for best compatibility - **WOFF (Web Open Font Format)** - Good compatibility - **OTF (OpenType)** - Good compatibility - **WOFF2** - Supported via automatic decompression to TTF ### ๐Ÿ”ง WOFF2 Decompression WOFF2 files are automatically decompressed to TTF format for processing: ```bash # Decompress WOFF2 manually if needed baseline-nudges decompress-woff2 input.woff2 output.ttf ``` The setup process automatically downloads Fira Sans in TTF format for maximum compatibility. ## JavaScript API ```javascript const { BaselineNudgeGenerator, generateFromConfig, } = require("@lyubomir-popov/baseline-nudge-generator"); // Generate from config file const result = await generateFromConfig( "./config/typography-config.json", "./dist" ); // Or use the class directly const generator = new BaselineNudgeGenerator(); const result = await generator.generateFiles( "./config/typography-config.json", "./dist" ); ``` ## CLI Commands ```bash # Setup project with font download baseline-nudges setup # Generate tokens and HTML demo baseline-nudges generate config/typography-config.json # Generate to specific output directory baseline-nudges generate config/typography-config.json custom-output/ # Create example configuration (downloads font automatically) baseline-nudges init # Create example configuration with custom name baseline-nudges init my-typography # Validate configuration file baseline-nudges validate config/typography-config.json # Legacy SCSS generation (backward compatibility) baseline-nudges generate-legacy config/typography-config.json output.scss # Watch mode for legacy SCSS (automatic regeneration) baseline-nudges watch config/typography-config.json output.scss # Show help baseline-nudges --help # Show version baseline-nudges --version ``` ## Requirements - Node.js 14.0.0 or higher - Font file in WOFF2, WOFF, TTF, or OTF format (required) ## License MIT ## Contributing Issues and pull requests are welcome. Please ensure your contributions include tests and documentation. ## Related Projects - [Vanilla Framework](https://vanillaframework.io) - The CSS framework this tool originally supported - [OpenType.js](https://opentype.js.org/) - Font parsing library used for metrics extraction - [Inter Font](https://rsms.github.io/inter/) - Used in examples and testing