Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A utility function in a CSS Variable #50189

Open
WraithKenny opened this issue Apr 28, 2023 · 10 comments
Open

A utility function in a CSS Variable #50189

WraithKenny opened this issue Apr 28, 2023 · 10 comments
Labels
[Feature] Typography Font and typography-related issues and PRs Needs Technical Feedback Needs testing from a developer perspective. [Type] Enhancement A suggestion for improvement.

Comments

@WraithKenny
Copy link

WraithKenny commented Apr 28, 2023

What problem does this address?

There's numerous places where "responsive" calculations are used, involving adjusting values based on screen sizes.

This includes, but is not limited to, responsive "fluid" fonts. The current solution is fairly complicated, and not particularly intuitive, with little opportunity for end users to adjust.

I propose a utility function var() that would respond to inherited css variables, that would simplify things, and also make it more extensible.

(The current implementation of Fluid Fonts seems to be written in PHP, but could actually be written in CSS.)

What is your proposed solution?

Naming of variables aside, the basic gist is shown by

// Explanation of the relevant maths.
:root {
	// User facing variables, unitless, measured in pixels
	--max-view: 1600; // Large breakpoint
	--min-view: 960; // Small breakpoint
	--max-size: 24; // Large size
	--min-size: 16; // Small size

	// Coercion to px units.
	--max-view-unit: calc(var(--max-view) * 1px); // unused, but here for completeness.
	--min-view-unit: calc(var(--min-view) * 1px);
	--max-size-unit: calc(var(--max-size) * 1px);
	--min-size-unit: calc(var(--min-size) * 1px);

	// The formula.
	--rem-base: calc(var(--min-size) / 16 * 1rem);
	--diff: calc((var(--max-size) - var(--min-size)) / (var(--max-view) - var(--min-view)));
	--the-calc: calc(var(--rem-base) + ( ( 100vw - var(--min-view-unit) ) * var(--diff) ));

	// The output.
	--the-clamp: clamp(var(--min-size-unit), var(--the-calc), var(--max-size-unit));
}

Condensed version:

:root {
	--max-view: 1600;
	--min-view: 768;
	--max-size: 25;
	--min-size: 10;
}

// calc() needs to be evalutated in scope, so cannot be defined at :root
* {
	--the-clamp: clamp(calc(var(--min-size) * 1px), calc((var(--min-size) / 16 * 1rem) + ( ( 100vw - (var(--min-view) * 1px) ) * ((var(--max-size) - var(--min-size)) / (var(--max-view) - var(--min-view))) )), calc(var(--max-size) * 1px));
}

How WP and End Users (theme and plugin devs) could use this

// From twentytwentythree.
h1 {
	// font-size: clamp(2.719rem, 2.719rem + ((1vw - 0.48rem) * 1.742), 3.625rem);
	--max-size: 58;
	--min-size: 43.504;
	font-size: var(--the-clamp);
}
.my-font-size {
	--max-size: 25;
	--min-size: 10;

	// Change the smaller breakpoint
	--min-view: 960;

	font-size: var(--the-clamp);
}

Additionally, it allows for this:

<div class="my-font-size" style="--max-size:50">Lorem Ipsum</div>

This part is important, as it shows how easily Core can inject custom values from the editor.


As shown here, we can achieve what we want to do with Fluid Fonts in a simpler, more intuitive way, that's also extensible, and written in CSS instead of PHP.

It created a mechanism that allows a way to adjust the breakpoint values simple by setting a local CSS Var.

Additionally, this utility can be used to adjust margins and paddings in a responsive way.

Also, this pattern of writing calc() formulas that depend on var()'s is more broadly applicable, in any place that you'd want to allow local var()s to adjust values. Simple formulas can be written for Colors to enable section color themes, for example. (I'm sure there's a solution to responsive column breakpoints somewhere in this technique too.)

This issue is a demonstration of the power of this technique, which I have seen utilized in many places, and I hope you'll consider the technique in the future. Thanks.

@WraithKenny
Copy link
Author

Related #49931

@WraithKenny
Copy link
Author

Related #38601

@WraithKenny
Copy link
Author

WraithKenny commented Apr 28, 2023

For naming convention for functional custom properties, and their local variables, maybe a name --wp--func--[func-name] and --wp--local--[func-name]--[var-name], with --wp--local--[global-name]--[var-name] for more general vars meant for local overrides by potentially more functions:

:root {
	// Breakpoint vars.
	--wp--local--breakpoint--large: 1600;
	--wp--local--breakpoint--small: 768;

	// Fluid function vars.
	--wp--local--fluid--max-size: 25;
	--wp--local--fluid--min-size: 10;

	--wp--func--fluid: clamp(calc(var(--wp--local--fluid--min-size) * 1px), calc((var(--wp--local--fluid--min-size) / 16 * 1rem) + ( ( 100vw - (var(--wp--local--breakpoint--small) * 1px) ) * ((var(--wp--local--fluid--max-size) - var(--wp--local--fluid--min-size)) / (var(--wp--local--breakpoint--large) - var(--wp--local--breakpoint--small))) )), calc(var(--wp--local--fluid--max-size) * 1px));
}

@WraithKenny
Copy link
Author

The naming conventions will be the most difficult part, as is always the case.

@WraithKenny
Copy link
Author

WraithKenny commented Apr 28, 2023

Turns out, this doesn't work unless the function is repeated, so never mind.

I had been testing this (the math) in the local scope. The thing that doesn't work is hosting the clamp/calc functionality to the :root, because apparently, it reads the variables from that scope, rather than at the local scope. That's very disappointing, since we'd have to define and apply a css class (or use scss extend) to get it to evaluate properly at the local scope.

Unless anyone knows a work around.

@WraithKenny
Copy link
Author

WraithKenny commented Apr 28, 2023

I've found a work-around, that might be less than satisfactory.

The functional custom properties either need to be applied to a class, which is then added to the desired element, or it needs to be added to a universal selector.

I'll reopen this for consideration.

My actually working, and more thoroughly tested code:

:root {
	--ash--local--breakpoint--large: 1600;
	--ash--local--breakpoint--small: 960;

	// Adjusts to REM.
	--ash--local--fluid-rem--max-size: 25;
	--ash--local--fluid-rem--min-size: 10;

	// Ignores REM.
	--ash--local--fluid--max-size: 25;
	--ash--local--fluid--min-size: 10;
}

// calc involving var need to be evaluated in scope, so :root is not usable.
* {
	// Adjusts to REM.
	--ash--func--fluid-rem: clamp(
		calc(var(--ash--local--fluid-rem--min-size) * 1px),
		calc((var(--ash--local--fluid-rem--min-size) / 16 * 1rem) + ( ( 100vw - (var(--ash--local--breakpoint--small) * 1px) ) * ((var(--ash--local--fluid-rem--max-size) - var(--ash--local--fluid-rem--min-size)) / (var(--ash--local--breakpoint--large) - var(--ash--local--breakpoint--small))) )),
		calc(var(--ash--local--fluid-rem--max-size) * 1px)
	);

	// Ignores REM.
	--ash--func--fluid: clamp(
		calc(var(--ash--local--fluid--min-size) * 1px),
		calc((var(--ash--local--fluid--min-size) * 1px) + ( ( 100vw - (var(--ash--local--breakpoint--small) * 1px) ) * ((var(--ash--local--fluid--max-size) - var(--ash--local--fluid--min-size)) / (var(--ash--local--breakpoint--large) - var(--ash--local--breakpoint--small))) )),
		calc(var(--ash--local--fluid--max-size) * 1px)
	);
}

Usage:

.my-menu > li {
	--ash--local--fluid--max-size: 15;
	--ash--local--fluid--min-size: 0;
	margin: 0 var(--ash--func--fluid);
}

@WraithKenny WraithKenny reopened this Apr 28, 2023
@bph bph added [Feature] Typography Font and typography-related issues and PRs Needs Technical Feedback Needs testing from a developer perspective. labels Apr 29, 2023
@ramonjd
Copy link
Member

ramonjd commented May 10, 2023

Thanks for opening up the issue, and for all the examples.

I agree that CSS vars open up a lot of flexibility.

The goal of the PHP implementation was to work on top of the existing theme.json framework and serve to convert any single font size value (px|em|rem) to a single "fluid" font-size value (clamp). The value could be not only used in global styles CSS, but inline block styles.

Where single values don't come with any min/max size, it tries to come up with a sensible scale factor by using a log function, which CSS doesn't yet support widely.

In that regard it's pretty flexible and doesn't require much tinkering by designers or developers who just want it "to work".

The PHP implementation is a bit of a closed system, that's true. And I could imagine the existing implementation working how it is, but printing out the global CSS vars for use by developers elsewhere.

The locally-scoped vars will probably be challenging to implement, not impossible though.

Another challenges I see is to ensure we reduce CSS bloat, especially where there are a lot of custom font sizes on a site.

To explore this, I'd probably start at the font presents (theme.json) and see how the output there could leverage CSS vars more widely.

@WraithKenny
Copy link
Author

Yes, reducing css bloat and extending the system to be easier to use is the goal here. Theme.json would be compatible with this idea, since it would simply print the values entered in theme.json but either in a css declaration for a class, as now, or in an inline style attribute. The bonus is that theme/plug-in devs could simply add their own declaration to change the font size, and still have it be fluid.

@WraithKenny
Copy link
Author

#51146 (comment)

@WraithKenny
Copy link
Author

WraithKenny commented Jun 20, 2023

Another benefit of this proposal, is that it would make customizing the Fluid Font Size via the Editor trivially easy for Core.

:root {
	--large: 1600;
	--small: 960;
	--max: 32;
	--min: 16;
	--rem: 16;
}

* {
	--func--fluid-rem: clamp(
		calc(var(--min) * 1px),
		calc((var(--min) / var(--rem) * 1rem) + ( ( 100vw - (var(--small) * 1px) ) * ((var(--max) - var(--min)) / (var(--large) - var(--small))) )),
		calc(var(--max) * 1px)
	);
}

.has-fluid-font-size {
	font-size: var(--func--fluid-rem);
}

/* Theme.json defined fluid size */
.has-xx-large-font-size {
	--max: 100;
	--min: 70;
}

Core could then take any values from a user, via an Editor panel asking for values of min, max font sizes, and values for small and large breakpoints, and simply print:

<div class="has-fluid-font-size" style="--max:24;--min:48;--small:480;--large:1200;">Lorem Ipsum</div>

User choosing xx-large would output:

<div class="has-fluid-font-size has-xx-large-font-size">Lorem Ipsum</div>

This proposal enables both full support for theme.json (and extends it while simplifying it) and enables support for Editor customizations that are (i think) currently blocked.

@jordesign jordesign added the [Type] Enhancement A suggestion for improvement. label Jul 20, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Typography Font and typography-related issues and PRs Needs Technical Feedback Needs testing from a developer perspective. [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

No branches or pull requests

4 participants