Aguarde...

25 de fevereiro de 2025

CSS @function + CSS if() = Explosão

CSS @function + CSS if() = Explosão

A busca por um light-dark()que funcione com qualquer valor.

A função que criei em https://brm.us/css-custom-functions é um costume --light-dark()que pode ser usado para retornar valores dependendo se o modo claro ou escuro está sendo usado.

@function --light-dark(--light, --dark) {
	result: var(--light);
	
	@media (prefers-color-scheme: dark) {
		result: var(--dark);
	}
}

Diferentemente do built-inlight-dark(), esta função personalizada não é limitada a <color>valores e funciona com qualquer tipo de valor. Mas também diferentemente light-dark()dela não pode responder ao color-schemevalor local e pode responder somente à preferência de mídia clara/escura.

Conforme sugerido no final do post, essa limitação pode ser removida quando o suporte para consultas de contêineres aninhados e/ou CSS if()for adicionado ao Chrome… e esse dia chegou!

Um costume --light-dark()usando consultas de contêiner

ℹ️ Como esse código usa consultas de contêiner, você sempre precisa de um elemento wrapper. A próxima seção que usaif() não precisa desse elemento wrapper extra.

Desde meu post anterior, o protótipo no Chrome foi expandido para também suportar consultas de contêineres aninhados dentro de funções personalizadas. Isso abre o caminho para permitir uma preferência de claro/escuro por elemento, assim:

  • Defina um esquema de cores preferencial em um elemento usando uma propriedade personalizada chamada--scheme
  • Retrabalhe --light-dark()para usar uma consulta de estilo para responder ao valor de--scheme

Os valores possíveis para --schemesão lightdark, e system. Quando --schemeé definido como um dos dois primeiros, o color-schemeé forçado para esse valor.

A função se parece com isso:

@function --light-dark(--light, --dark) {
	/* Default to the --light value */
	result: var(--light);
	
	/* If the container is set to "dark", use the --dark value */
	@container style(--scheme: dark) {
		result: var(--dark);
	}
}

Dentro de @function, os valores --light--darksão passados ​​como argumentos para a função. A --schemepropriedade personalizada, no entanto, é lida do elemento no qual a função é invocada.

Para garantir que haja algum valor para --scheme, eu o defino em :rootdependendo do prefers-color-schemevalor. O prefers-color-schemevalor também é duplicado em a --root-schemepara habilitar o suporte para um --schemevalor de system, mas falaremos mais sobre isso depois.

:root {
	--root-scheme: light;
	--scheme: light;

	@media (prefers-color-scheme: dark) {
		--root-scheme: dark;
		--scheme: dark;
	}
}

Para permitir a definição de um esquema de cores preferencial por elemento, recorri ao uso de um data-schemeatributo HTML que analiso para um valor em CSS usando attr(). Quando o valor é lightou darkeu uso o valor diretamente. Quando o valor é system, o código usa o --root-schemevalor da propriedade. Para jogar bem com contextos claro/escuro aninhados, o código usa @scope.

/* Allow overriding the --scheme from the data-scheme HTML attribute */
@scope ([data-scheme]) {
	/* Get the value from the attribute */
	:scope {
		--scheme: attr(data-scheme type(<custom-ident>));
	}
	
	/* When set to system, use the --root-scheme value (which is determined by the MQ) */
	:scope[data-scheme="system"] {
		--scheme: var(--root-scheme);
	}
	
	/* This allows the native light-dark() to work as well */
	:scope > * {	
		color-scheme: var(--scheme);
	}
	
	/* Because the elements with the attribute are extra wrapper elements, we can just display its contents */
	display: contents;
}

Para aprender sobre isso attr(), vá ler CSS attr()recebe uma atualização . Quanto a @scope, é suficiente ler a introdução rápida em@scope.

Com todas as peças no lugar, é hora de usá-lo.

Em CSS:

[data-scheme] > * {
	color: light-dark(#333, #e4e4e4);
	background-color: light-dark(aliceblue, #333);
	
	border: 4px --light-dark(dashed, dotted) currentcolor;
	font-weight: --light-dark(500, 300);
	font-size: --light-dark(16px, 18px);
	
	transition: all 0.25s ease, border-style 0.25s allow-discrete;
}

Em HTML:

<div data-scheme="light">
	<div class="stylable-thing">
		…
	</div>
</div>

Aqui está uma demonstração ao vivo. Lembre-se de que você precisa do Chrome Canary com o Experimental Web Platform Features Flag para ver o código em ação.

Um costume --light-dark()usando Inlineif()

A partir do Chrome Canary 135.0.7022.0, o inlineif() também está disponível atrás do sinalizador Experimental Web Platform Features. Graças a essa função, você pode omitir o elemento extra do contêiner que a abordagem de consultas do contêiner precisa, pois você pode selecionar condicionalmente um valor diretamente em uma declaração.

Como a if()função também aceita consultas de estilo como uma das condições, a abordagem geral permanece a mesma: use uma propriedade personalizada e responda ao seu valor. O código resultante, no entanto, é muito mais curto:

@function --light-dark(--light, --dark) {
	result: if(
		style(--scheme: dark): var(--dark);
		else: var(--light)
	);
}

Nota lateral: Você sabia que inline if()pode aceitar múltiplas condições? Assim:

color: if(
	style(--scheme: dark): var(--dark-color);
	style(--scheme: dim): var(--dim-color);
	else: var(--light)
);

Esse tipo de uso, no entanto, está além do escopo deste post.

O código para definir --schemeou lighttambém darké mais curto, pois é mais fácil retornar ao --root-schemevalor.

:root {
	--root-scheme: light;
	--scheme: light;

	@media (prefers-color-scheme: dark) {
		--root-scheme: dark;
		--scheme: dark;
	}
}	

@scope ([data-scheme]) {
	:scope {
		--scheme-from-attr: attr(data-scheme type(<custom-ident>));
		--scheme: if(
			style(--scheme-from-attr: system): var(--root-scheme);
			else: var(--scheme-from-attr)
		);
		color-scheme: var(--scheme); /* To make the native light-dark() work */
	}
}

O uso continua o mesmo de antes, com a diferença de que você pode definir os estilos dependentes do esquema de cores diretamente no [data-scheme]elemento.

[data-scheme] {
	color: light-dark(#333, #e4e4e4);
	background-color: light-dark(aliceblue, #333);
	
	border: 4px --light-dark(dashed, dotted) currentcolor;
	font-weight: --light-dark(500, 300);
	font-size: --light-dark(16px, 18px);
	
	transition: all 0.25s ease, border-style 0.25s allow-discrete;
}
<div class="stylable-thing" data-scheme="light">
	…
</div>

Aqui está uma demonstração ao vivo para você conferir:

~

Conclusão

Eu já estava muito animado com CSS Custom Functions por si só. Combiná-lo com inline if()leva isso a um nível ainda mais alto.

Postado em BlogTags:
Escreva um comentário