Note: I just translated the text below and posted it here. References are at the end of this article.
CSS is a highly specialized programming language focused on style systems. Because of this single use case and its declarative nature, it is sometimes difficult to understand. Some people even deny that it is a programming language. Let's prove them wrong by programming an intelligent and flexible style system.
More traditional and general-purpose languages (like JavaScript) give us tools like "Conditions" (if/then), "Loops" (for, while), "Logical Gates" (===, &&, etc.) and "Variables". These structures are named differently in CSS, their syntax is very different to better accommodate the specific use case of styling a document, and some of them simply weren't available in CSS until a few years ago.
Variables are the most straightforward. They are called Custom properties in CSS (although everyone calls them variables anyway, even their own syntax).
:root { --color: red; } span { color: var(--color, blue); }
The double dash declares a variable and assigns a value. This has to happen in a scope because doing this outside of a selector would break CSS syntax. Note the :root selector, which works as a global scope.
Conditions can be written in several ways, depending on where you want to use them. Selectors have scope to their elements, media queries have global scope and need their own selectors.
[data-attr='true'] { /* if */ } [data-attr='false'] { /* elseif */ } :not([data-attr]) { /* else */ }
:checked { /* if */ } :not(:checked) { /* else */ }
:root { color: red; /* else */ } @media (min-width > 600px) { :root { color: blue; /* if */ } }
Counters are the most direct form of loops in CSS, but also the one with the most restricted use case. You can only use counters on the content property, displaying it as text. You can adjust your "increment", your "starting point" and your "value" at any given point, but the output is always limited to text.
main { counter-reset: section; } section { counter-increment: section; counter-reset: section; } section > h2::before { content: 'Headline ' counter(section) ': '; }
But what if you wanted to use a loop to define a recurring layout pattern? This type of loop is a little more obscure: it is the auto-fill property of grids.
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
This fills the grid with as many elements as it can fit, while sizing them to fill the available space, but breaking them into multiple lines when necessary. It repeats as long as it finds grid items and limits them to a minimum width of 300px and a maximum width of a fraction of the size of their grid container. It's probably easier to see than to explain:
And finally, there are "looped selectors". They take an argument, which can be a formula to select very precisely.
section:nth-child(2n) { /* seleciona todos os elementos pares */ } section:nth-child(4n + 2) { /* seleciona a cada quarto items, iniciando a partir do segundo */ }
For really special cases, you can combine :nth-child() with :not(), like:
section:nth-child(3n):not(:nth-child(6)) { /* seleciona a cada 3 elementos, mas não o sexto elemento */ }
You can replace :nth-child() with :nth-of-type() and :nth-last-of-type() to change the scope of these last examples.
Ana Tudor wrote an article about CSS Logic Gates. Login Gates work on the idea of combining variables with calc. She then continues modeling and animating 3D objects with this. It sounds like arcane magic, it gets a lot more insane as the article goes on, and it's generally one of the best explanations of why CSS is in fact a programming language.
* + * { margin-top: 1rem; }
Owl Selector selects each item that follows an item. Applying a margin-top to this effectively adds a gap between items, like grid-gap does, but without the grid system. This also means it is more customizable. You can override your margin-top and adapt it to any type of content. Want to have 1rem of space between each item, but 3rem before a title? This is easier to do with an owl selector than in a grid.
Kevin Pennekamp has an in-depth article on this that even explains his algorithm in pseudocode.
Podemos criar toggles em nosso código css que ligam e desligam certas regras com variables e calc. Isso nos dá condições muito versáteis.
.box { padding: 1rem 1rem 1rem calc(1rem + var(--s) * 4rem); color: hsl(0, calc(var(--s, 0) * 100%), 80%); background-color: hsl(0, calc(var(--s, 0) * 100%), 15%); border: calc(var(--s, 0) * 1px) solid hsl(0, calc(var(--s, 0) * 100%), 80%); } .icon { opacity: calc(var(--s) * 100%); transform: scale(calc(var(--s) * 100%)); }
Dependendo do valor de --s, .box habilitará ou desabilitará seus alert styles.
Vamos levar a mesma lógica um passo adiante e criar uma color variable que depende do seu contraste com a background color:
:root { --theme-hue: 210deg; --theme-sat: 30%; --theme-lit: 20%; --theme-font-threshold: 51%; --background-color: hsl(var(--theme-hue), var(--theme-sat), var(--theme-lit)); --font-color: hsl( var(--theme-hue), var(--theme-sat), clamp(10%, calc(100% - (var(--theme-lit) - var(theme-font-threshold)) * 1000), 95%) ); }
Este snippet calcula um background color a partir de valores HSL e uma font color black ou white, invertendo o valor de lightness (luminosidade) do background. Isso por si só pode resultar em baixo contraste de cor (uma fonte cinza de 40% em um background cinza de 60% é praticamente ilegível), então subtrairei um valor threshold (o ponto em que a cor muda de white para black), multiplicarei por um valor insanamente alto como 1000 e farei clamp nele entre 10% e 95%, para obter uma porcentagem de lightness válida no final. Tudo é controlável editando as quatro variáveis no início do snippet.
Este método também pode ser usado para escrever lógica de cores intrincada e themes automáticos, com base apenas em valores HSL.
Vamos combinar o que temos até agora para limpar a stylesheet. Ordenando tudo por viewports parece um pouco espaguete, mas ordenar isso por componente não parece nada melhor. Com variables, podemos ter o melhor dos dois mundos:
/* define variables */ :root { --paragraph-width: 90ch; --sidebar-width: 30ch; --layout-s: "header header" "sidebar sidebar" "main main" "footer footer"; --layout-l: "header header" "main sidebar" "footer footer"; --template-s: auto auto minmax(100%, 1fr) auto / minmax(70%, var(--paragraph-width)) minmax(30%, var(--sidebar-width)); --template-l: auto minmax(100%, 1fr) auto / minmax(70%, var(--paragraph-width)) minmax(30%, var(--sidebar-width)); --layout: var(--layout-s); --template: var(--template-s); --gap-width: 1rem; } /* manipula variables por viewport */ @media (min-width: 48rem) { :root { --layout: var(--layout-l); --template: var(--template-l); } } /* realiza o bind no DOM */ body { display: grid; grid-template: var(--template); grid-template-areas: var(--layout); grid-gap: var(--gap-width); justify-content: center; min-height: 100vh; max-width: calc( var(--paragraph-width) + var(--sidebar-width) + var(--gap-width) ); padding: 0 var(--gap-width); }
Todas as global variables são definidas no topo e ordenadas por viewport. Essa seção efetivamente se torna a Definition of Behavior, esclarecendo questões como:
Abaixo estão as definições de regras, ordenadas por componente. As Media Queries não são mais necessárias aqui, porque elas já estão definidas no topo e colocadas em variables. Podemos simplesmente codificar em nossas stylesheets sem interrupções neste ponto.
Um caso especial de pseudo classes é o :target selector, que pode ler o hash fragment da URL. Aqui está uma demonstração que usa essa mecânica para simular uma experiência semelhante a SPA:
Eu escrevi um post sobre isso. Só esteja ciente de que isso tem algumas implicações sérias de acessibilidade e precisa de alguma mecânica JavaScript para realmente ser livre de barreiras. Não faça isso em um live environment.
Manipular CSS variables se tornou uma ferramenta muito poderosa agora. Também podemos aproveitar isso em JavaScript:
// configura --s em :root document.documentElement.style.setProperty('--s', e.target.value); // configura --s scoped para #myID const el = document.querySelector('#myID'); el.style.setProperty('--s', e.target.value); // lê variables de um element const switch = getComputedStyle(el).getPropertyValue('--s');
Os exemplos de codepen acima funcionam exatamente assim.
CSS é muito capaz de definir layout systems inteligentes e reativos. Suas estruturas de controle e algoritmos podem ser um pouco estranhos em comparação com outras linguagens, mas eles estão lá e estão à altura da tarefa. Vamos parar de apenas descrever alguns styles e começar a fazer eles funcionar.
Artigo escrito por Daniel Schulz
The above is the detailed content of Writing Logic in CSS. For more information, please follow other related articles on the PHP Chinese website!