Skip to content

Dynamic Observed ( ? )

The ? marker puts a selector in dynamic observed mode.


  • if in the DOM on first compilation → generated
  • if not in the DOM on first compilation → listens to the DOM
  • if later added to the DOM → generated
  • if later removed from the DOM → rule is deactivated

The ? marker is written at the very end of the selector part, without a space. If a pseudo is present, it comes after the pseudo.

.toast? { background: #333; }
#modal? { display: flex; }
.btn:hover? { background: #1a50e0; }
/* wrong */
.btn?:hover { background: #1a50e0; }
.toast ? { background: #333; }

The same rule applies after composition via &var(...):

--hovercard: :hover { transform: translateY(-2px); };
--active: .is-active { box-shadow: 0 10px 26px rgba(22, 61, 120, 0.18); };
.card &var(--hovercard)? { opacity: 0.85; }
.card &var(--active)? { opacity: 0.85; }

These examples are evaluated as .card:hover? and .card.is-active? respectively.


.toast? {
background: #1a1a2e;
color: #fff;
padding: 8px 16px;
border-radius: 6px;
}

If .toast is not in the DOM when the page opens, this rule is not generated. If .toast is later added via JavaScript, the rule is generated at that point.


SituationUsageWhyExample
Component to be added later via JavaScriptUse ?May not be in the DOM on first open but can come later.toast? { background: #333; }
Modal opened via Ajax / client-side renderUse ?Modal or overlay may be added to DOM later.modal? { display: flex; }
Notification classes that come based on stateUse ?Which variant will come is not certain in advance.toast-success? { ... }
Cards that multiply later via infinite scroll / filteringUse ?List items may grow over time.product-card? { border: 1px solid #ddd; }
Utility/grid classes that are fixed on page openPrefer ?!Dynamic watching creates unnecessary cost.col-4?! { grid-column: span 4; }
Static header/footer always in the DOMDon't use ObservedNormal selector is more correct and cheaperheader { background: #fff; }

Short note:

  • If there's a chance of being added to the DOM later → ?
  • If only the initial snapshot matters → ?!
  • Don't use observed for static selectors that are always present

.notification? { background: #2060ff; color: #fff; padding: 12px 16px; }
.toast-error? { background: #991b1b; color: #fff; padding: 8px 12px; }
.toast-success? { background: #065f46; color: #fff; padding: 8px 12px; }
.modal? {
position: fixed;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
}
.overlay? {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
}
--variants: [info, success, warning, danger];
@for(v, var(--variants)) {
.alert-${v}? {
border-left: 4px solid var(--colors.${v});
padding: 12px 16px;
border-radius: 4px;
}
}

This pattern is suitable for variant components that can be added to the DOM later.

--mode: wide;
@if(var(--mode) === 'wide') {
#panel? { display: block; }
}
else {
#panel? { display: none; }
}

The condition is resolved at compile time; only the true branch is carried to runtime.


When lazy is on, when dynamic observed rules are compiled and when they activate may change. For the detailed behavior table, see the Performance page.