Skip to content

Initial Observed ( ?! )

The ?! marker puts a selector in initial observed mode. It looks at the DOM snapshot at page open; it does not watch elements added later.


  • if in the DOM on first compilation → generated
  • if not in the DOM on first compilation → not generated
  • if later added to the DOM → not generated
  • if later removed from the DOM → generated CSS stays active

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.

.col-4?! { grid-column: span 4; }
.span-2?! { grid-column: span 2; }
.btn:focus?! { outline: 2px solid #2060ff; }
/* wrong */
.btn?!:focus { outline: 2px solid #2060ff; }
.col-4 ?! { grid-column: span 4; }

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.


@for(i, [1-12]) {
.col-${i}?! {
grid-column: span ${i};
}
}

When the page opens, only the .col-* selectors that are present in the DOM are generated. CSS is not generated for new .col-* elements that come later.


SituationUsageWhyExample
Grid classes that are fixed on page openUse ?!Initial snapshot is sufficient, no need to watch later.col-4?! { grid-column: span 4; }
Producing only the used part of large utility setsUse ?!Generates as much CSS as the actual usage at open.mt-4?! { margin-top: 16px; }
Fixed card variants from SSR or server renderUse ?!DOM is ready on first load, no later additions expected.card-compact?! { padding: 8px; }
Visible part of theme / palette utility classes at openUse ?!Emitted as much as the first observed usage.text-primary?! { color: var(--colors.primary); }
Toast, modal, drawer that open laterPrefer ?They will be added to DOM outside the initial snapshot, so ?! is insufficient.toast? { background: #333; }
Basic layout rules that must always be generatedDon't use ObservedUse a normal selector if you don't want snapshot filtering.container { max-width: 1200px; }

Short note:

  • If the opening DOM snapshot is sufficient → ?!
  • If there are selectors to be added to the DOM later → ?
  • For static CSS needed in all cases, don't use observed

@for(i, [1-12]) {
.col-${i}?! {
grid-column: span ${i};
}
}
@for(i, [1-6]) {
.row-${i}?! {
grid-row: span ${i};
}
}
--palette: [primary, secondary, success, warning, danger];
@for(color, var(--palette)) {
.text-${color}?! { color: var(--colors.${color}); }
.bg-${color}?! { background: var(--colors.${color}); }
.border-${color}?! { border-color: var(--colors.${color}); }
}
@for(i, [0-12]) {
.mt-${i}?! { margin-top: ${i * 4}px; }
.mb-${i}?! { margin-bottom: ${i * 4}px; }
.p-${i}?! { padding: ${i * 4}px; }
}
--layout: compact;
@if(var(--layout) === 'compact') {
.card-compact?! { padding: 8px; }
}
else {
.card-compact?! { padding: 16px; }
}

Only the true branch is generated; the generated selector also only enters CSS if it has a match in the opening snapshot.


When lazy is on, the behavior of initial observed rules at the initial load time gains extra importance. For the detailed behavior table, see the Performance page.