Date: 2025-04-25
In web development, declarative means expressing desired outcomes using standard browser features—without scripting how to achieve them.
You describe what the browser should do. The browser handles how to do it.
By contrast, imperative code tells the browser step-by-step how to perform a task using JavaScript or external logic.
Declarative features are:
- 🧬 Native: Part of HTML/CSS specifications
- 🚀 Performant: Handled directly by the rendering engine
- ♿ Accessible: Align with browser + assistive tech behaviors
- 🧹 Maintainable: Easier to read, scale, and debug
Declarative doesn’t mean limited—browsers continue to add expressive power (e.g. :has()
, container queries, content visibility, native validation).
<form>
<label for="email">Email</label>
<input id="email" type="email" required>
<button>Submit</button>
</form>
<input id="email">
<button onclick="validate()">Submit</button>
<script>
function validate() {
const email = document.getElementById('email')
if (!email.value) {
alert('Email is required')
}
}
</script>
<details>
<summary>Advanced Settings</summary>
<fieldset>
<label><input type="checkbox"> Enable Logs</label>
</fieldset>
</details>
<button onclick="toggle()">Advanced Settings</button>
<div id="settings" hidden>
<label><input type="checkbox"> Enable Logs</label>
</div>
<script>
function toggle() {
const el = document.getElementById('settings')
el.hidden = !el.hidden
}
</script>
form:has(input:invalid) button {
opacity: 0.5;
pointer-events: none;
}
This CSS-only rule disables the submit button until all inputs are valid—no JS required.
✅ Benefit | 🌟 How It Helps |
---|---|
🔄 Predictability | Browsers already know how to render and handle native behavior |
♿ Accessibility | Works out-of-the-box with AT (e.g. screen readers) |
🧹 Maintenance | Fewer moving parts, easier audits |
🚀 Performance | No runtime JS for basic behavior |
📱 Interop | Compatible across modern devices and input types |
Imperative logic may still be necessary for:
- 🗄️ Data layer logic (e.g. CRUD ops, fetch, parse, cache)
- 🧠 Business rules (e.g. role-based access, pricing conditions)
- 🌐 External integrations (e.g. analytics, API gateways)
- 💾 Non-DOM state (e.g. service workers, app-level persistence)
🧠 Note: GUI state is now fully declarative.
Modern CSS handles all visual and interactive UI states natively using:
:has()
:checked
,:valid
,:invalid
,:empty
:target
,:focus-within
,:not(:empty)
- Container queries (
@container
,container-type
) - Style queries (
@container style(...)
) - Logical properties (e.g.
block-size
,inline-size
)
This eliminates the need for JS-based state machines, toggling classes, or imperative DOM manipulation for GUI state.
-
Declarative Programming and the Web – Smashing Magazine
Explores declarative paradigms in web development, contrasting them with imperative logic. -
Declarative Shadow DOM – web.dev
Details how modern browsers implement declarative shadow DOM for encapsulated UI components. -
HTML: A Good Basis for Accessibility – MDN Web Docs
Emphasizes how semantic HTML contributes to accessibility by design. -
Declarative Design – Jeremy Keith
Advocates for native HTML and CSS as the foundation for resilient, maintainable web interfaces.
🔒 Minimizing external dependencies—beyond the native browser—is a strategic choice for long-term stability and maintainability. The browser is our one true dependency. Relying on it directly enables longevity, future-proofing, and avoids entanglements with third-party tooling that may change, deprecate, or fragment over time.
Declarative development isn’t about avoiding code—it’s about writing less of it by leveraging built-in capabilities.
It leads to faster, more stable, and more accessible interfaces, while preserving full control over business logic where needed.