@guetechteam/font-adjustment
v1.2.3
Published
User-adjustable text size (default / medium / large) via a CSS variable, with React hook, no-flash SSR script and CSS generator.
Downloads
860
Readme
@guetechteam/font-adjustment
User-adjustable text size (default / medium / large). Sets --font-scale and data-font-scale
on <html>; it does not change the root font-size, so spacing and layout stay the same.
Text classes follow the variable through a generated CSS file.
Install
npm i @guetechteam/font-adjustmentCore
import { setFontScale, getStoredFontScale, subscribeFontScale, scaleHtmlFontSize } from '@guetechteam/font-adjustment';
setFontScale('large'); // persists the step name, applies it, notifies this tab
subscribeFontScale((step) => {}); // this tab + other tabs; returns unsubscribe
dangerouslySetInnerHTML={{ __html: scaleHtmlFontSize(apiHtml) }} // inline px/pt sizes follow the variableCustom steps, key or names:
import { createFontScale } from '@guetechteam/font-adjustment';
export const fontScale = createFontScale({
steps: { default: 100, medium: 120, large: 150 }, // percent, smallest -> largest
storageKey: 'FONT_SCALE',
});Pass the same config to getFontScaleScript and useFontScale (below).
React
import { useFontScale } from '@guetechteam/font-adjustment/react';
const { scale, setScale, increase, decrease, reset, canIncrease, canDecrease } = useFontScale();
// custom config: useFontScale(fontScale)react is an optional peer dependency.
No-flash script (SSR)
Inline in <head> so the saved step applies before hydration:
import { getFontScaleScript } from '@guetechteam/font-adjustment/script';
<Head><script dangerouslySetInnerHTML={{ __html: getFontScaleScript() }} /></Head>Ready-made CSS
For apps that use the d2d classes (Tailwind with the tw- prefix, body-*/label-*/title-*/headline-*, legacy .font-N), import the bundled file after your global CSS:
// pages/_app.js
import '../public/assets/css/global.css';
import '@guetechteam/font-adjustment/font-scale.css';It is generated from font-scale.config.mjs by npm run build:css (part of npm run build).
To add or change a class, edit that file and rebuild.
CSS
Bundled stylesheet
The package ships a ready-made font-scale.css (built from font-scale.config.mjs at the package root).
Import it once, after your global CSS:
import '@guetechteam/font-adjustment/font-scale.css'; // e.g. in _app.jsIt scales the Tailwind tw-text-* / tw-leading-* classes, the design-system classes (body-*, label-*,
title-*, headline-*, m3-display-*), the legacy .font-N classes and the body default
(--bs-body-font-size, fallback 0.875rem). Rules use !important under html[data-font-scale].
Adding a class (maintainers): add a rule to font-scale.config.mjs, then run npm run build
(build:css regenerates dist/font-scale.css). Classes are listed by hand. Rules:
- Only list classes whose base size is defined elsewhere (an override of an undefined class changes its size even at 100%).
- Copy the original base values exactly so the default step looks unchanged.
- Numbers are px (converted to rem), strings are used as-is. Escape selectors (
.tw-text-\\[14px\\]).
{ selector: '.title-22-medium', fontSize: 22, lineHeight: '1.75rem' }Generating your own CSS
If your app has a different class set, skip the bundled file and generate your own:
// fontScale.config.mjs
export default {
rules: [
{ selector: '.tw-text-sm', fontSize: 14, lineHeight: 20 },
{ selector: '.tw-leading-4', lineHeight: '1rem' },
],
body: { variable: '--bs-body-font-size', fallback: '0.875rem' },
};npx font-adjustment-css fontScale.config.mjs -o public/assets/css/fontScale.cssOr call generateFontScaleCss(options) from @guetechteam/font-adjustment/css.
Text weight
A second, independent setting (like iOS Larger Text and Bold Text). Same shape as size, own key, event and attribute:
| Setting | Storage key | Attribute | CSS variables |
|---|---|---|---|
| size | FONT_SCALE | data-font-scale | --font-scale |
| weight | FONT_WEIGHT | data-font-weight | --fw-400, --fw-500, --fw-600, --fw-700 |
Weight cannot be multiplied: fonts load static weights (Roboto: 100, 300, 400, 500, 700, 900), so a computed
450 would snap to a neighbour. Each step instead maps a base weight to a weight:
| Step | 400 | 500 | 600 | 700 | |---|---|---|---|---| | default | 400 | 500 | 600 | 700 | | medium | 500 | 700 | 700 | 700 | | large | 700 | 700 | 900 | 900 |
Roboto has no 600, so the browser renders a 600 as 700. The 600 base (tw-font-semibold) stays 600 at default
(unchanged), then 700 and 900, so large is visibly heavier for it too.
This mapping is a draft, confirm it with design. Override it with createFontWeight({ steps, ... })
(every step must define the same base weights).
import { setFontWeight, getStoredFontWeight, subscribeFontWeight, weightHtmlFontWeight } from '@guetechteam/font-adjustment';
setFontWeight('large');
weightHtmlFontWeight(apiHtml); // inline font-weight: 400 / bold -> var(--fw-400, 400) / var(--fw-700, 700)import { useFontWeight } from '@guetechteam/font-adjustment/react';
const { weight, setWeight, increase, decrease, reset, canIncrease, canDecrease } = useFontWeight();// one inline script for both settings (or getFontScaleScript / getFontWeightScript separately)
import { getFontAdjustmentScript } from '@guetechteam/font-adjustment/script';
<script dangerouslySetInnerHTML={{ __html: getFontAdjustmentScript() }} />import '@guetechteam/font-adjustment/font-weight.css'; // after global CSSThe bundled font-weight.css has one rule per class (font-weight: var(--fw-<base>, <base>) !important), so each
step only changes the variables. It covers tw-font-normal/medium/semibold/bold, the design-system classes
(body-* regular, label-* and title-* medium, headline-* regular or bold) and b, strong.
Text with no weight class, such as a bare tw-text-sm, inherits the body base weight (400) and so follows the step too.
This only works when nothing pins a weight on the element itself: a rule such as div { font-weight: 400 } in your
global CSS matches every <div> directly, so it beats inheritance from body and from the parent, and plain div/p
text never follows. Point such rules at the variable instead: font-weight: var(--fw-400, 400) (the fallback keeps it
unchanged while no step is active).
Not covered: tw-font-light, body-10-medium, m3-display-*,
Bootstrap h1-h6. Add classes in font-weight.config.mjs (base weight must match the original), then npm run build.
For your own class list: npx font-adjustment-css my-weight.config.mjs --weight -o fontWeight.css
with { rules: [{ selector: '.x', fontWeight: 400 }] }, or generateFontWeightCss from /css.
Notes
- Exempt both storage keys (
FONT_SCALE,FONT_WEIGHT) from anylocalStorage.clear()/ reset logic in your app. - Fixed
pxheights/widths can clip text at larger steps; check visually atlarge. - Heavier text is wider: check wrapping and clipping at weight
large, in fixed-width boxes especially. - Check that every weight the steps can produce is in your loaded font files (e.g. 900 for Roboto, 500/700 for the API HTML font).
