Skip to content

Commit a7d9a20

Browse files
committed
update the recipe for Fluid Typography
1 parent 2347e62 commit a7d9a20

File tree

1 file changed

+98
-12
lines changed

1 file changed

+98
-12
lines changed

README.md

+98-12
Original file line numberDiff line numberDiff line change
@@ -155,19 +155,98 @@ This implementation is inspired by [Tailwind CSS’s `css-functions.js`](https:/
155155

156156
### Fluid Typography
157157

158-
You can also implement [Fluid Typography](https://www.smashingmagazine.com/2022/01/modern-fluid-typography-css-clamp/) as a custom function, using the [`tan(atan2())` technique](https://dev.to/janeori/css-type-casting-to-numeric-tanatan2-scalars-582j) to remove px units and calculate them in CSS.
158+
You can also implement [Fluid Typography](https://www.smashingmagazine.com/2022/01/modern-fluid-typography-css-clamp/) as a custom function.
159159

160160
```js
161+
function round(n) {
162+
return Math.round((n + Number.EPSILON) * 10000) / 10000;
163+
}
164+
165+
function fluid(
166+
min,
167+
max,
168+
minBreakpoint = '640',
169+
maxBreakpoint = '1536',
170+
...rest
171+
) {
172+
if (!min || !max) {
173+
throw new Error(
174+
'The --fluid(…) function requires at least 2 arguments, but received insufficient arguments.',
175+
);
176+
}
177+
178+
if (rest.length > 0) {
179+
throw new Error(
180+
`The --fluid(…) function only accepts 4 arguments, but received ${
181+
rest.length + 1
182+
}.`,
183+
);
184+
}
185+
186+
min = Number(min);
187+
max = Number(max);
188+
minBreakpoint = Number(minBreakpoint);
189+
maxBreakpoint = Number(maxBreakpoint);
190+
191+
const divider = 16;
192+
const slope =
193+
(max / divider - min / divider) /
194+
(maxBreakpoint / divider - minBreakpoint / divider);
195+
const intersection = -1 * (minBreakpoint / divider) * slope + min / divider;
196+
197+
return `clamp(${[
198+
`${(min > max ? max : min) / divider}rem`,
199+
`${round(intersection)}rem + ${round(slope * 100)}svw`,
200+
`${(min > max ? min : max) / divider}rem`,
201+
].join(', ')})`;
202+
}
203+
204+
module.exports = {
205+
plugins: {
206+
'@yuheiy/postcss-custom-functions': {
207+
functions: {
208+
'--fluid': fluid,
209+
},
210+
},
211+
},
212+
};
213+
```
214+
215+
Use the custom function you have defined:
216+
217+
```css
218+
h1 {
219+
font-size: --fluid(32, 64);
220+
}
221+
```
222+
223+
will be processed to:
224+
225+
```css
226+
h1 {
227+
font-size: clamp(2rem, 0.5714rem + 3.5714svw, 4rem);
228+
}
229+
```
230+
231+
Also, by utilizing the [`tan(atan2())` technique](https://dev.to/janeori/css-type-casting-to-numeric-tanatan2-scalars-582j), we can perform calculations in a CSS-native way without requiring JavaScript unit conversions. This means we can combine different units and work with custom properties directly in CSS.
232+
233+
> **Note:** Currently, this technique does not work in Firefox.
234+
235+
```js
236+
function toPx(length) {
237+
return `tan(atan2(${length}, 1px))`;
238+
}
239+
161240
function fluid(
162-
minSize,
163-
maxSize,
241+
min,
242+
max,
164243
minBreakpoint = 'var(--breakpoint-sm)',
165-
maxBreakpoint = 'var(--breakpoint-xl)',
244+
maxBreakpoint = 'var(--breakpoint-2xl)',
166245
...rest
167246
) {
168-
if (!minSize || !maxSize) {
247+
if (!min || !max) {
169248
throw new Error(
170-
'The --fluid(…) function requires 2–4 arguments, but received none.',
249+
'The --fluid(…) function requires at least 2 arguments, but received insufficient arguments.',
171250
);
172251
}
173252

@@ -179,13 +258,14 @@ function fluid(
179258
);
180259
}
181260

182-
const slope = `calc(tan(atan2(${maxSize} - ${minSize}, 1px)) / tan(atan2(${maxBreakpoint} - ${minBreakpoint}, 1px)))`;
183-
const intercept = `calc(tan(atan2(${minSize}, 1px)) - ${slope} * tan(atan2(${minBreakpoint}, 1px)))`;
261+
const t = `(${toPx('100svw')} - ${toPx(minBreakpoint)}) / (${toPx(
262+
maxBreakpoint,
263+
)} - ${toPx(minBreakpoint)})`;
184264

185265
return `clamp(${[
186-
`min(${minSize}, ${maxSize})`,
187-
`${slope} * 100svw + ${intercept} / 16 * 1rem`,
188-
`max(${minSize}, ${maxSize})`,
266+
`min(${min}, ${max})`,
267+
`${min} + (${max} - ${min}) * ${t}`,
268+
`max(${min}, ${max})`,
189269
].join(', ')})`;
190270
}
191271

@@ -229,6 +309,12 @@ will be processed to:
229309
}
230310

231311
h1 {
232-
font-size: clamp(min(2rem, 4rem), calc(tan(atan2(4rem - 2rem, 1px)) / tan(atan2(var(--breakpoint-xl) - var(--breakpoint-sm), 1px))) * 100svw + calc(tan(atan2(2rem, 1px)) - calc(tan(atan2(4rem - 2rem, 1px)) / tan(atan2(var(--breakpoint-xl) - var(--breakpoint-sm), 1px))) * tan(atan2(var(--breakpoint-sm), 1px))) / 16 * 1rem, max(2rem, 4rem));
312+
font-size: clamp(
313+
min(2rem, 4rem),
314+
2rem + (4rem - 2rem) *
315+
(tan(atan2(100svw, 1px)) - tan(atan2(var(--breakpoint-sm), 1px)))
316+
/ (tan(atan2(var(--breakpoint-2xl), 1px)) - tan(atan2(var(--breakpoint-sm), 1px))),
317+
max(2rem, 4rem)
318+
);
233319
}
234320
```

0 commit comments

Comments
 (0)