Architecture Decisions
Every major architectural choice in Vanilla Breeze traces to a failure mode it prevents. This page documents those decisions so future contributors understand not just what was decided, but why. For the philosophy these decisions serve, see Principles.
Hyphenated Element Names
- Decision
- Custom elements must use a hyphen in their name (e.g.
<tab-set>, not<tabset>). - Because
- The HTML spec reserves unhyphenated names for future standard elements. Using a hyphen prevents collision with any current or future HTML element, ensures the parser routes the element through the custom element registry, and makes third-party component origins visually identifiable in markup.
- Trade-off
- Slightly more verbose tag names. Acceptable given the collision protection and parser guarantee.
State via data-* Attributes
- Decision
- Component state is communicated via
data-*attributes, not CSS classes. - Because
- CSS classes conflate styling concerns with state concerns.
data-*attributes are queryable via thedatasetAPI, can be targeted by CSS attribute selectors without class specificity issues, survive serialization (e.g. server-side rendering produces correct initial state), and are unambiguous in their purpose. A class namedactivecould mean anything;data-state="active"cannot. - Trade-off
- Slightly more verbose selectors (
[data-state="active"]vs.active). Acceptable given the clarity and API benefits.
Single @layer Declaration
- Decision
@layerorder is declared once in the framework entry file; no other file may redeclare layer order.- Because
- Multiple
@layerorder declarations in different files produce unpredictable cascade behavior as load order varies. A single declaration point guarantees deterministic cascade behavior regardless of which stylesheets load first or in what order. This is the CSS equivalent of a single source of truth. - Trade-off
- All consuming projects must import the framework entry file before any other stylesheet. Documented as a usage requirement.
Theme Restrictions
- Decision
- Themes may not change layout, spacing structure, or
displayvalues — only color, typography, and surface tokens. - Because
- Layout changes in a theme layer break the component's pre-upgrade CSS baseline, potentially causing the
:not(:defined)fallback and the upgraded state to have different layout models. This would produce layout shift on upgrade even with pre-upgrade CSS in place. - Trade-off
- Theme authors have less control. This is intentional — visual identity lives in color and type; structural layout lives in components.
<details>-Based Tabs
- Decision
- The
<tab-set>component is built on native<details>elements with thenameattribute for exclusive accordion behavior. - Because
- Native
<details>provides disclosure interactivity without JavaScript, keyboard support, screen reader announcements, and form state restoration for free. Thenameattribute (supported in all modern browsers) provides exclusive-open behavior. This means tabs degrade gracefully to a working accordion when JS fails to load. - Trade-off
- Summary elements cannot use ARIA
role="tab"without conflicting with their implicit button role. VB accepts this by relying on the native semantics rather than imposing tab semantics.
OKLCH Color System
- Decision
- All color tokens use OKLCH color space with hue-angle custom properties.
- Because
- OKLCH provides perceptually uniform lightness (unlike HSL), consistent chroma across hues, and enables algorithmic palette generation via hue rotation. The
@property-registered hue angles (--hue-primary, etc.) allow themes to shift the entire palette with a single value change. - Trade-off
- OKLCH requires modern browser support (2023+). Fallback hex values are provided where critical.
Pack Architecture
- Decision
- Visual themes, effects, and component extensions are bundled as "packs" (e.g.
kawaii,retro) that layer onto the base framework. - Because
- Packs allow themed experiences without polluting the core framework. Each pack appends to the declared
bundle-theme,bundle-effects, andbundle-componentslayers, ensuring predictable cascade behavior. Packs are additive and optional — the framework is fully functional without any pack. - Trade-off
- Pack authors must understand the layer system and token contract. Documentation and the
/validate-componentcommand help enforce compliance.
Custom Elements over Utility Classes
- Decision
<layout-stack data-layout-gap="m">instead of<div class="flex flex-col gap-4">.- Because
- Custom elements are self-documenting in the DOM inspector, clearly show nesting relationships, and don't pollute the global class namespace. They create a semantic vocabulary for layout intent rather than encoding visual properties in markup.
- Trade-off
- Slight unfamiliarity for developers used to utility-class frameworks. The readability and maintainability benefits outweigh this learning curve.
Logical Properties Throughout
- Decision
- All component CSS uses logical properties (
inline-size,margin-block-start,inset-inline-end) instead of physical equivalents. - Because
- Physical CSS properties (
margin-left,width,top) break silently in RTL layouts and non-horizontal writing modes. Logical properties adapt automatically to any writing direction, making components work correctly in English (LTR), Arabic (RTL), and vertical scripts without code changes. - Trade-off
- Less familiar syntax. Intentional exceptions exist for
float: left(limitedfloat: inline-startsupport), CSS Anchor Positioning (uses physical inset properties), and JS-computed viewport coordinates.
Selective Normalization, Not Reset
- Decision
- Vanilla Breeze uses targeted normalization rather than a comprehensive CSS reset.
- Because
- Browser defaults are often sensible and reflect user preferences (e.g. font size, link colors). A heavy reset discards those defaults and forces re-declaration of everything. Selective normalization adjusts only what needs adjusting (box-sizing, margin collapse, tap targets), resulting in smaller CSS and better alignment with user settings.
- Trade-off
- Components inherit some browser variation. Acceptable because design tokens override the meaningful values.
Zero Runtime Dependencies
- Decision
- Vanilla Breeze ships no runtime dependencies. The CSS and JavaScript are self-contained.
- Because
- Every dependency is a supply chain risk, a source of breaking changes, and a chunk of code you don't control. Zero dependencies means no transitive vulnerabilities, no forced upgrades, no package manager required, and a stable artifact that works as long as browsers support standard web APIs.
- Trade-off
- Some functionality that libraries provide for free must be implemented directly. Acceptable given the security, stability, and longevity benefits.
MIT License
- Decision
- The project uses the MIT license.
- Because
- MIT is maximally permissive. Students can use it in coursework. Companies can use it without legal review. Contributors face no licensing friction. This removes barriers to adoption and encourages learning.
- Trade-off
- No copyleft protection. Acceptable because the goal is maximum adoption and educational use.
Pre-upgrade CSS Baseline
- Decision
- Every web component must include
:not(:defined)CSS rules that render content readably before JavaScript upgrades the element. - Because
- The gap between HTML parsing and custom element upgrade produces a Flash of Undefined Content (FOUC). Without pre-upgrade styles, elements may render as unstyled blocks, shift layout on upgrade, or be invisible. The
:not(:defined)selector targets exactly this window. - Trade-off
- Component authors must maintain two visual states (pre and post upgrade). The spec template and validation command enforce this requirement.