Skip to content

Instantly share code, notes, and snippets.

@hgl
Last active August 29, 2015 14:18
Show Gist options
  • Save hgl/77f0141eea04817ee0af to your computer and use it in GitHub Desktop.
Save hgl/77f0141eea04817ee0af to your computer and use it in GitHub Desktop.

Problem

Given this css code

:root {
	--color: #aaa;
	--bg-color: var(--color);
	--narrow-window-width: 30em;
}
body {
	background: var(--bg-color);
}
@custom-media --narrow-window (max-width: var(--narrow-window-width));
@media (--narrow-window) {
  body {}
}

Would like to extract computed values of custom properties definitions:

{
	"custom-properties": {
		"color": "#aaa",
		"bg-color": "#aaa",
	},
	"custom-media-queries": {
		"narrow-window-width": "30em"
	}
}

Why not pass in these values from outside?

We can of cause solve the problem with external values:

var color = "#aaa";
var props = {
	"color": color,
	"bg-color": color
},
cssnext(cssString, {
	features: { variables: props }
});

However, it doesn't scale well when the dependencies between values is deep:

var pageWidth = 960;
var sidebarWidth = pageWidth / 3;
var sidebarSearchWidth = sidebarWidth * 0.8;
var sidebarSearchButtonWidth = sidebarSearchWidth * 0.2;
var props = {
	"page-width": `${pageWidth}`px,
	"sidebar-width": `${sidebarWidth}px`,
	"sidebar-search-width": `${sidebarSearchWidth}px`
	"sidebar-search-button-width": `${sidebarButtonWidth}px`
},

The amount of bolierplate code is huge, comparing writing them directly in css:

:root {
	--page-width: 960px;
	--sidebar-width: calc(var(--page-width) / 3);
	--sidebar-search-width: calc(var(--sidebar-width) * 0.8);
	--sidebar-search-button-width: calc(var(--sidebar-search-width) * 0.2);
}

How to extract these values?

We leave the custom properties/custom media queries defintions in ast, and when cssnext done processing the ast, we extract values from these definitions. The processed ast should equivalent to this

:root {
	--color: #aaa;
	--bg-color: #aaa;
	--narrow-window-width: 30em;
}
body {
	background: #aaa;
}
@custom-media --narrow-window (max-width: 30em);
@media (max-width: 30em) {
  body {}
}

We then iterate over :root rules and @custom-media rules to extract the values.

What need to be implemented?

postcss-custom-properties offers a preserve option, but it duplicates definitions, and postcss-custom-media doesn't offer such feature. Currently enabling the option compiles (or is supposed to compile) to this:

:root {
	--color: #aaa;
	--bg-color: #aaa;
	--bg-color: var(--color);
	--narrow-window-width: 30em;
}
body {
	background: #aaa;
	background: var(--bg-color);
}
@media (max-width: 30em) {
  body {}
}

The duplication can be solved by only using the last custom properties definition of that name that doesn't contain var(). For postcss-custom-media, we need to implement a similar option that compiles this:

@custom-media --narrow-window (max-width: 30em);
@media (--narrow-window) {
  body {}
}

to this

@custom-media --narrow-window (max-width: 30em);
@media (max-width: 30em) {
  body {}
}

That is, not removing the definition.

Other things to fix

postcss-custom-properties currently doesn't resovle var() usages in media queries (custom or regular), so we need to add that. Also we add the preserve option to postcss-custom-media. Enabling these options should yield:

:root {
	--color: #aaa;
	--bg-color: #aaa;
	--bg-color: var(--color);
	--narrow-window-width: 30em;
}
body {
	background: #aaa;
	background: var(--bg-color);
}
@custom-media --narrow-window (max-width: 30em);
@custom-media --narrow-window (max-width: var(--narrow-window-width));
@media (max-width: 30em) {
  body {}
}
@media (--narrow-window) {
  body {}
}

As you can see, this could potentially result in duplicating much code (especially by duplicating media rules, not to mention it might compound with prefixes) without bringing any real benefits (browsers support custom-* features always compute to the same value, if the value is directly resolvable). We can either keep that, or introduce a break change, making preserve option not to keep the specified value:

:root {
	--color: #aaa;
	--bg-color: #aaa;
	--narrow-window-width: 30em;
}
body {
	background: #aaa;
}
@custom-media --narrow-window (max-width: 30em);
@media (max-width: 30em) {
  body {}
}

This means preserve simply preserves definitions. Users who want to retain the specified values should simply disable the plugins. I doubt the var() fallback brings any real benefits.


Another thing to fix is that postcss-custom-properties currently choke on infinite variable referencing:

:root {
	--color: var(--color);
	--bg-color: var(--bg2-color);
	--bg-color: var(--bg-color);
}

It should detect this and throw an error. This can be fixed by caching resolved values. It should also greatly speed up resolving process, since every value is now only resolved once.

@WolfgangKluge
Copy link

postcss-custom-properties currently doesn't resovle var() usages in media queries

Have a look at https://github.com/WolfgangKluge/postcss-media-variables

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment