Last active
April 29, 2024 04:39
-
-
Save vnphanquang/6a73786eecfbfedb6c4be64a09517fb5 to your computer and use it in GitHub Desktop.
Fluid Calculator (for Typography)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @public | |
* @typedef {'rem' | 'px'} Unit | |
*/ | |
/** | |
* @public | |
* @typedef {`${number}${Unit}`} Length | |
*/ | |
/** | |
* @public | |
* @typedef ClampFluidConfig | |
* @property {[Length, Length]} screen_range | |
* @property {[Length, Length]} value_range | |
* @property {number} root_font_size_px | |
*/ | |
/** | |
* @public | |
* generate a {@link https://developer.mozilla.org/en-US/docs/Web/CSS/clamp | CSS clamp} expression for scaling value between a screen range | |
* | |
* @param {ClampFluidConfig} config | |
*/ | |
export function clamp_fluid(config) { | |
const { | |
screen_range: [screen_min, screen_max], | |
value_range: [value_min, value_max], | |
root_font_size_px, | |
} = config; | |
let screen_min_px = resolve_length_to_px(screen_min, root_font_size_px); | |
let screen_max_px = resolve_length_to_px(screen_max, root_font_size_px); | |
let screen_delta_px = screen_max_px - screen_min_px; | |
let value_min_px = resolve_length_to_px(value_min, root_font_size_px); | |
let value_max_px = resolve_length_to_px(value_max, root_font_size_px); | |
let value_delta_px = value_max_px - value_min_px; | |
const vw = value_delta_px * 100 / screen_delta_px; | |
const rem = (value_min_px - (screen_min_px * value_delta_px / screen_delta_px)) / root_font_size_px; | |
if (Math.abs(rem) < 1) { | |
console.warn(`The calculated rem value "${rem}" has its absolute value less than 1. It should be at least 1rem so that users can scale it effectively. Try changing the input config`); | |
} | |
const min = format_length(value_min_px / root_font_size_px) + 'rem'; | |
const max = format_length(value_max_px / root_font_size_px) + 'rem'; | |
const ideal = rem > 0 | |
? `${format_length(vw)}vw + ${format_length(rem)}rem` | |
: `${format_length(vw)}vw - ${format_length(Math.abs(rem))}rem` | |
return `clamp(${min}, ${ideal}, ${max})`; | |
} | |
/** | |
* @internal | |
* @param {Length} value | |
* @param {number} root_font_size_px | |
*/ | |
function resolve_length_to_px(length, root_font_size_px) { | |
if (length.includes('rem')) { | |
return parseFloat(length) * root_font_size_px; | |
} | |
return parseFloat(length); | |
} | |
/** | |
* @param {number} length | |
*/ | |
function format_length(length) { | |
if (Math.round(length) !== length) { | |
return length.toFixed(3); | |
} | |
return length.toString(); | |
} | |
// test | |
const config = { | |
screen_range: ['400px', '1400px'], | |
value_range: ['2rem', '4.5rem'], | |
root_font_size_px: 16, | |
}; | |
const clamped = clamp_fluid(config); | |
// assert clamped = clamp(2rem, 4vw + 1rem, 4.5rem) | |
console.log(clamped); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment