A making-of field note
The site was built to grow in.
Verdance Commons needed to feel less like an energy dashboard and more like the shared conservatory where that system becomes part of daily life. The page makes its own frame as you move through it, while soft, refracted light passes across the paper.
A useful kind of optimism.
Most energy interfaces isolate a meter reading from the people who depend on it. This fictional commons does the opposite: solar, storage, repair, gardens, and dinner tables are presented as one looping civic practice. The warm atrium in the opening scene is not decoration; it is an imagined place where that infrastructure is visible, legible, and social.
The layouts deliberately change pace: an illustrated entry, a numerical ledger, a circular current diagram, a daily timetable, and resident notes. That variety keeps the story moving without making the reader rush.
Vines that draw their own borders.
Every major section owns a real inline SVG frame. On load, each path measures its own length with getTotalLength(), then begins completely hidden via stroke-dashoffset. An IntersectionObserver adds the growth class only when that frame crosses into view. Leaves are separate SVG paths, scaling in on a stagger so they appear to bud from a finished stem.
Not an image.
Watch the line arrive.
const vineFrames = document.querySelectorAll('[data-vine]');
const grow = (frame) => frame.classList.add('is-grown');
const reveal = (frame) => requestAnimationFrame(() =>
requestAnimationFrame(() => grow(frame))
);
vineFrames.forEach((frame) => {
try {
frame.querySelectorAll('.vine-stem').forEach((path) => {
const length = path.getTotalLength();
if (!Number.isFinite(length) || length <= 0) throw new Error('Invalid vine path length');
path.style.strokeDasharray = `${length} ${length}`;
path.style.strokeDashoffset = length;
});
} catch (error) { grow(frame); }
});
const vineObserver = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) { reveal(entry.target); observer.unobserve(entry.target); }
});
}, { threshold: .18, rootMargin: '0px 0px -6% 0px' });
The other ambient layer is a fixed canvas. It draws a handful of slowly drifting radial pools and elliptical highlights using additive blending. It is deliberately faint: light should make the page feel inhabited, never get in the way of the copy. The animation stops in a hidden tab, caps pixel density at two, and becomes a single still frame for reduced-motion visitors. The final daylight dispatch is deliberately not an animation: visitors choose an hour and the illustrated atrium reallocates its daylight, all the way through a quiet after-dark state.
const canAnimate = () => !reducedMotion && pageVisible && canvasInView;
const animate = (time) => {
if (!canAnimate()) { rafId = 0; return; }
drawCaustics(time);
rafId = requestAnimationFrame(animate);
};
document.addEventListener('visibilitychange', () => {
pageVisible = !document.hidden;
updateCanvasState();
});
Sun through leaves, held by bark.
The limited palette is coded as CSS variables and repeats in the illustration, SVG frames, and printed-card layouts. Dark bark carries the reading weight; the brighter colours are reserved for signals of growth, warmth, and circulation.
Cormorant Garamond
Display face — softly formal, botanical without becoming theatrical.
Nunito Sans
Body face — rounded humanist clarity for the practical details.
What broke, what changed.
Pass 1 — correctness
- Made the 390px meter heading stack instead of retaining a 230px minimum side column, and tightened the compact header so it keeps a 44px navigation target without clipping.
- Added shrink guards to grid children and 44px minimum targets to navigation, utility, footer, and guide links.
- Strengthened small text that used low-contrast leaf or terracotta tones, added a high-contrast visible focus treatment, and hid duplicate decorative quote marks from assistive technology.
- Hardened vine setup against an unavailable SVG path length, delayed its first reveal by two frames so the drawing transition is visible, and fell back to a complete frame.
- Paused the canvas and CSS loops for hidden tabs, reduced-motion preferences, and off-screen animated sections; the canvas now also observes its own visibility and keeps device pixel ratio capped at two.
- Replaced the reserved .example mail link with a real internal guide link, and confirmed there are no image assets or runtime fetches. Browser automation was unavailable in that pass, so the 390px, 768px, and 1440px checks began as a source and layout-constraint audit.
Pass 2 — design depth
- Weak moment: the middle sections kept the same safe, two-column cadence. The energy loop now breaks out into a full-bleed current field with an oversized, lifted diagram and content that crosses its edge; the daily rhythm remains the quieter editorial counterpoint.
- Weak moment: type hierarchy flattened after the hero. A named spacing scale now governs the main beats, display headings use stronger tracking and balanced line breaks, and each major section has a deliberate display size instead of sharing one timid scale.
- Weak moment: the vine frame repeated as a cloned corner ornament. The current field carries a longer, eight-leaf art-nouveau tendril that grows across the section, so the signature technique now changes character with the story rather than merely repeating.
Pass 3 — complexify
- Weak moment: daylight was described, but never handed to the visitor. The new Daylight Dispatch lets visitors choose five moments in one ordinary day. Its glasshouse illustration moves the sun, battery fill, demand line, and copy as one quiet state change rather than a generic widget.
- Edge state: after dark is not an empty state. At 20:30 the sun drops away, the pane settles into evening colour, and the copy explains what the stored afternoon still keeps running. The range input and pressed button state make the same moment reachable by mouse, touch, or keyboard.
- Final finish: the line must arrive before the vine. A tiny head script applies the hidden stroke state before first paint, then exact path lengths take over in the main script. Fonts use an optional loading strategy, Open Graph fields now identify the fictional commons, and the final source audit checks responsive constraints, motion fallbacks, and script syntax.