Zum Hauptinhalt springen
Echtzeit-Radar & Feeds
Alle RSS Feeds ➔
👥 Community & Social
Windows Tipps & SecurityGrafikkarte vor Überhitzung schützen: So geht’s(25.09.2026 um 08:00 Uhr)
••••••••••
Windows Tipps & SecurityGrafikkarte vor Überhitzung schützen: So geht’s(25.09.2026 um 08:00 Uhr)
••••••••••
Intelligence View
⚡ tsecurity.de Intelligence

Why I spent years trying to make CSS states predictable

Have you ever changed the order of two CSS rules and broken a component without changing the logic? .btn:hover { background: dodgerblue; } .btn[disabled] { background: gray; } Both selectors have specificity (0, 1, 1). When a…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

Have you ever changed the order of two CSS rules and broken a component without changing the logic?




.btn:hover     { background: dodgerblue; }
.btn[disabled] { background: gray; }






Both selectors have specificity (0, 1, 1). When a button is both hovered and disabled, the browser falls back to source order. If the :hover rule comes last, the disabled button turns blue. If the [disabled] rule comes last, it stays gray.



That sounds small, but it points to a bigger problem: component state in CSS often works by overlap.



As long as a component has only one or two states, that overlap feels manageable. Once you add :hover, :active, disabled, dark mode, breakpoints, data attributes, container queries, and overrides, it stops feeling manageable very quickly. You are no longer just writing styles. You are maintaining a resolution system in your head.



And that showed up not only as accidental conflicts, but as a growing difficulty in customizing existing components safely as real requirements piled up.



That was the problem I kept running into while building component systems. Not on toy examples, but on real buttons, inputs, panels, dropdowns, and design-system primitives. The hardest part was not writing the first version of a component. It was extending it later without reopening the entire state-resolution problem.



At some point I stopped asking, "How do I write this selector?" and started asking a better question:



What if component state could be expressed declaratively, while the compiler handled the selector logic needed to make it deterministic?



That question eventually became Tasty.






The idea in one minute



Instead of writing selectors that compete through cascade and specificity, I wanted to describe a property's possible states as a map:




import { tasty } from '@tenphi/tasty';

const Button = tasty({
as: 'button',
styles: {
fill: {
'': '#primary',
':hover': '#primary-hover',
':active': '#primary-pressed',
'[disabled]': '#surface',
},
},
});






Applied in order of priority, this means:




  • when disabled use #surface

  • otherwise, on active use #primary-pressed

  • otherwise, on hover use #primary-hover

  • otherwise use #primary



The important part is what happens next.



Tasty compiles that state map into selectors that cannot overlap:




/* [disabled] wins outright */
.t0[disabled] { background: var(--surface-color); }
/* :active is excluded when disabled */
.t0:active:not([disabled]) { background: var(--primary-pressed-color); }
/* :hover is excluded when :active or disabled */
.t0:hover:not(:active):not([disabled]) { background: var(--primary-hover-color); }
/* default is excluded when anything above matches */
.t0:not(:hover):not(:active):not([disabled]) { background: var(--primary-color); }






Now there is no argument for the cascade to settle. No two branches can match at the same time.



And the real payoff comes later. Extending or changing this map is far easier than reopening the equivalent selector logic in traditional CSS.



That is the whole idea:



If the author already defined the priority, the generated selectors should make that priority unambiguous.






Why this matters more than the button example suggests



A hovered disabled button is just the easiest way to see the problem. The real pain starts when states intersect in less obvious ways.



Maybe dark mode can come from a root attribute, or from prefers-color-scheme, or from both. Maybe spacing changes inside a narrow container, but only on tablet widths. Maybe a destructive variant behaves differently on hover but not when loading. Maybe a parent theme toggles a child override.



Each one of those rules is understandable in isolation. The difficult part is the interaction surface between them.



That interaction surface is where CSS starts feeling fragile. Small edits can change which branches overlap. A harmless refactor can turn into a source-order bug. Extending an existing component can mean reopening selector logic you thought was already settled.



I wanted a model where adding a new state did not mean mentally re-deriving the whole selector matrix.






Why it took so long



The core idea is simple. Turning it into a real tool was the hard part.



Getting from "this works for simple state conditions" to "this can support real-world component systems" took several years and hundreds of iterations.



The hard part was never producing one clever selector. The hard part was building a system that stayed coherent when all of these showed up together:




  • pseudo-classes like :hover and :active

  • attributes, boolean modifiers, and value-based modifiers

  • root-level state

  • media queries

  • container queries

  • nested and compound selectors

  • extending styles and overriding them safely

  • typed APIs on top of the styling model



Every time the model got broader, I had to check whether the original idea still held up. Sometimes it did. Sometimes it very much did not.



There were stretches where I had to break the DSL, rethink how states should be represented internally, and rebuild large parts of the compiler to preserve the same promise: if the author defines priority, the generated selectors should make that priority unambiguous.



Some of the difficulty was technical. Some of it was conceptual.



The technical side was about parsing, normalization, selector generation, caching, extension rules, and making the output fast enough to be practical.



The conceptual side was harder. I had to keep deciding what Tasty actually was.



Was it a nicer CSS object format? An atomic CSS generator? A design-system language? A compiler for stateful component styles? In practice it kept becoming all of those at once, which meant the boundaries had to be redrawn again and again before the whole thing felt internally consistent.



For a long time I honestly did not know whether the idea could scale cleanly enough to justify the effort. It worked in pockets early. Turning it into something I could trust across a design system was the long part.



And this is not just an experiment in the abstract. Tasty has powered Cube UI Kit from the beginning. That system now spans 100+ components and powers Cube Cloud, a real enterprise product. Early versions were absolutely experimental internally. But the model earned its shape under production pressure and team feedback.






The part I care about most



I do not think "mutually exclusive selectors" are interesting because they are clever.



I think they are interesting because they remove a category of ambiguity that should not be the author's job in the first place.



When I style a component, I want to describe what it should look like in each meaningful state. I do not want to manually encode the browser's tie-breaker logic every time those states intersect.



That is the payoff Tasty is chasing:




  • predictable component behavior

  • fewer accidental regressions from source order

  • easier extension of existing components

  • a styling model that gets more valuable as the design system gets more complex



If you are styling a small landing page, this is probably too much machinery. Plain CSS is often the right answer.



But if you are building components that need to survive years of iteration, variant growth, theme expansion, and multiple authors, predictability starts compounding in a very practical way.






A slightly bigger example



Here is the same idea with a few more moving parts:




const Panel = tasty({
styles: {
flow: {
'': 'column',
'@media(w >= 768px)': 'row',
},
fill: {
'': '#surface',
'theme=danger & :hover': '#danger-hover',
'@root(schema=dark)': '#surface-dark',
},
padding: {
'': '4x',
'@(sidebar, w < 300px)': '2x',
},
},
});






This is the point where I find the model becomes more useful than ordinary selector authoring.



Three properties, each with a different set of concerns — media queries, container queries, modifiers, root state, pseudo-classes — and the author never has to think about how they interact with each other. The compiler already knows.






What this post is, and what it is not



This is not the full Tasty tour.



Tasty also has typed component APIs, sub-elements, SSR integrations, zero-runtime extraction, editor tooling, linting, tokens, recipes, and more. Those all matter, and they are part of why the tool is useful in practice.



But they are downstream of the main idea.



The main idea is still this:



component states should be easy to describe and hard to make ambiguous.



That sentence took years to turn into a tool I was comfortable releasing.






If this resonates



You can try Tasty in the browser with the playground, or read the docs if you want the full language and feature set.



If you do try it, I would genuinely love feedback. The most useful feedback is rarely "this is cool." It is usually something more specific:




  • where the model clicked immediately

  • where it felt unfamiliar

  • where naming was confusing

  • where the docs skipped a mental step

  • where the abstraction solved a real problem, or failed to



That kind of feedback has shaped the project from the beginning, and it still does. If something feels confusing, awkward, or missing, the best place to share it is GitHub Issues.



If you made it all the way to the end, thank you for reading. This one means a lot to me, because it is really about a problem I spent years trying to solve.



Links: Docs | Playground | GitHub | npm

1. Sofort-Triage & Abwehrmaßnahmen

SOC Incident Playbook: Remote Code Execution (RCE) Defense
Syntax validiert (0 Fehler)
title: Detect Exploitation - Why I spent years trying to make CSS states predictable
id: a0ec60f6-0601-4021-8011-f08747ce9aef
status: experimental
description: Automatisch generierte SIEM-Erkennungsregel basierend auf CTI Intelligence
references:
  - https://tsecurity.de/
author: iShareStuff CTI Automated Detection Engine
date: 2026-09-26
logsource:
  category: network_connection
  product: any
detection:
  selection:
      CommandLine|contains:
        - 'exploit'
  condition: selection
falsepositives:
  - Legitime administrative Zugriffe oder Penetrationstests
level: high
tags:
  - attack.initial_access
Syntax validiert (0 Fehler)
rule CTI_Threat_Indicator {
    meta:
        author = "iShareStuff CTI Automated Detection Engine"
        date = "2026-09-26"
        description = "YARA Signature for "
    strings:
        $str = "Why I spent years trying to ma" ascii wide
    condition:
        any of them
}
Syntax validiert (0 Fehler)
index=security sourcetype IN ("cisco:asa", "pan:traffic", "zeek_conn", "suricata", "WinEventLog:Security")
("Why I spent years trying to make CSS sta")
| stats count earliest(_time) as first_seen latest(_time) as last_seen by src_ip, dest_ip, dest_host, signature
| eval first_seen=strftime(first_seen, "%Y-%m-%d %H:%M:%S"), last_seen=strftime(last_seen, "%Y-%m-%d %H:%M:%S")
| sort - count
Syntax validiert (0 Fehler)
message: "*Why I spent years trying to make CSS sta*"
Syntax validiert (0 Fehler)
CommonSecurityLog
| where Message has "Why I spent years trying to make CSS sta"
| summarize EventCount = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated) by SourceIP, DestinationIP, DestinationPort, Activity
| extend DetectionRule = "iShareStuff-CTI-Compiled"
| sort by EventCount desc

2. Cyber Threat Intelligence & Forensik

🎯
MITRE ATT&CK Matrix Navigator 14 Taktiken
Reconnaissance
-
Resource Development
-
Initial Access
Execution
Persistence
-
Privilege Escalation
Defense Evasion
Credential Access
-
Discovery
-
Lateral Movement
-
Collection
-
Command and Control
Exfiltration
-
Impact
tsecurity.de Cognitive Threat RAG
Fokus-Vektor:

Kognitive Analyse für identifizierte Bedrohung: Erhöhte Bedrohungslage im Bereich Why I spent years trying to make CSS sta.... Basierend auf 368k Vektor-Korrelationen werden sofortige Isolationsmaßnahmen für betroffene Endpunkte empfohlen.

🛡️ Angriffsfläche & Exposure

Netzwerk/Remote-Zugriff ohne Vorauthentifizierung möglich.

⚡ Empfohlene Sofortmaßnahmen
  • 1. Perimeter-Inspektion: Relevante Portfreigaben und exponierte Endpunkte unverzüglich scannen.
  • 2. Patch-Applikation: Hersteller-Hotfix einspielen oder betroffene Daemons in isolierte DMZ-Segmente überführen.
  • 3. Telemetrie & EDR-Alerts: Prozessaufrufe und Child-Processes auf anomale Shell-Spawns überwachen.
🔗 Semantisch verwandte Zero-Days MariaDB 11.7 VEC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Why I spent years trying to make CSS states predictable

Thematisch verwandte Begriffe: spent, years, trying, make · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-100620 | Capgo CLI (npm package @capgo/cli) through 7.98.2 is affected by an ove…
Advisory →
tsecurity.de Icon
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag