Changelog
What shipped, and when.
Every release — versioned, dated, and honest about breaking changes.Subscribe to get notified of major updates.
Minorv1.4.0
Dark / Light Mode
- Complete dark/light mode system. A ThemeToggle (sun/moon button) appears in the desktop nav and the mobile drawer. The chosen theme persists in localStorage under 'zutra:color-scheme' and survives full page loads, View Transitions navigations, and layout switches between Layout.astro and AppLayout.astro.
- Anti-FOUC inline scripts in both Layout.astro and AppLayout.astro restore accent color and color scheme before the first paint, preventing any flash of wrong theme on load or hard refresh.
- View Transitions persistence via astro:before-swap: theme and accent CSS custom properties are applied to the incoming document before Astro swaps the DOM, eliminating the flash that previously occurred when navigating between pages.
- Animated theme transition: switching themes triggers a smooth 280ms crossfade (background-color, color, border-color, box-shadow, fill, stroke) by temporarily adding a .theme-transitioning class to <html>. The class is removed after 350ms so hover and focus transitions are not affected. Disabled automatically under prefers-reduced-motion.
- Comprehensive light mode CSS overrides in global.css covering: all Tailwind alpha-white border and background utilities, zinc background/text variants, hardcoded dark hex backgrounds (#050507 through #111115), announcement bar tinted variants, badge colors (emerald / amber / red / blue), status page banners, pricing table (feature matrix, plan cards, billing switch), purchase banner, FAQ accordion open-state, footer dividers, and hover/group-hover text utilities.
- ThemePicker: contrast panel border, random accent lightness range, tooltip background, and scrollbar thumb now adapt correctly to light mode.
- THEME config block added to src/config/features.ts: colorScheme (true/false, shows or hides the toggle) and defaultTheme ('dark' | 'light', sets the fallback when no preference is stored).
- demoDropdown feature flag enabled by default (true). The 'Pages' dropdown now appears in the desktop nav, showing all template demo pages.
- Mobile nav: language switcher hidden in the top nav bar on mobile (the mobile drawer already has its own full language grid — the two were appearing simultaneously).
- Mobile nav: pages accordion (.mobile-nav-summary class) was missing its CSS definition, causing the browser's native <summary> disclosure triangle to show instead of the designed style. Definition added.
- i18n: fixed SiteFooter LINK_KEYS mapping for 'Customers' — was incorrectly 'nav.company.customers', corrected to 'footer.company.customers'. The footer was rendering the raw key string instead of the translated label.
- i18n: added footer.legal.title key to all 6 locale nav.json files (en: Legal, es: Legal, fr: Légal, de: Rechtliches, pt: Legal, pl: Prawne). The key was referenced in SiteFooter but did not exist, causing the raw key string to render as the Legal column heading.
- Bug fix: theme toggle became unresponsive after any View Transitions page navigation. Root cause — click listeners were attached directly to the button element on astro:page-load, but the initialized guard prevented re-binding after the button was replaced by the swap. Fixed by moving to event delegation: a single click listener on document checks e.target.closest('#zutra-theme-toggle'), surviving all DOM replacements.
Breaking changes
- THEME is now exported from src/config/features.ts and re-exported from src/config.ts. If you import from src/config directly this is transparent. If you have a custom features.ts without THEME, add: export const THEME = { colorScheme: true, defaultTheme: 'dark' as const }.
Minorv1.3.0
Astro 7 Upgrade
- Upgraded to Astro 7.0.3 (from 6.4.8), @astrojs/mdx 7.0.0, and @astrojs/node 11.0.0.
- Removed Storybook — no config directory existed and the addon/core versions were mismatched (core at v10, addons at v8). Dependency tree is cleaner and peer-dep warnings are gone.
- Fixed missing await on checkRateLimit() in all 5 API routes (/api/waitlist, /api/contact, /api/auth/login, /api/auth/signup, /api/auth/forgot-password). Rate limiting was silently not working.
- Fixed Badge variant 'error' → 'danger' in the dashboard logs panel to match the Badge component's allowed variants.
- Worker-src blob: CSP directive now emitted in dev mode only, suppressing the Vite HMR console warning about blob: workers violating CSP.
- 0 TypeScript errors, 0 warnings across 243 files. 69 tests passing.
Minorv1.2.0
i18n Overhaul — 6 Languages
- Six full locales now ship out of the box: English, Spanish, French, German, Portuguese (Brazilian), and Polish. All languages have complete translations across all 5 i18n files.
- Translation JSON split from two monolithic files (en.json / es.json) into 5 focused files per locale — nav.json, hero.json, sections.json, auth.json, app.json — making it easy to find and edit any string.
- New I18N config flag in src/config/features.ts: multiLanguage (true/false) and defaultLanguage. Set multiLanguage: false to run a single-language site and hide the switcher entirely.
- Language switcher redesigned: desktop dropdown now shows native language names in a 2-column grid. Button shows the full native name on medium screens and the locale code on small screens.
- Mobile drawer now includes a language selector — a 3-column grid of all supported locales with accent highlight on the active one.
- Fixed rootPath locale-stacking bug: switching language multiple times was appending locale prefixes (/es/de, /pl/de/es). Now uses stripLocale() so the root path is always clean.
- 3-tier architecture enforced: all visible text lives in i18n (including marketing copy, CTAs, purchase banner, and announcement bar). Config holds only structural/behavioral non-text data.
- Announcement bar text and link label moved from config/nav.ts to i18n — translated in all 6 locales.
- Purchase banner label, badge, CTA, and price note moved from config/features.ts to i18n — translated in all 6 locales. URL and price remain in config.
- English pages about.astro and customers.astro updated to use t() for CTA section — consistent with the Spanish equivalents that already used i18n correctly.
- 69 tests passing (up from 43 in v1.0.0, 68 in v1.1.0).
Breaking changes
- HERO.badge removed from config/hero.ts — badge text now lives in i18n/en/hero.json (and es, fr, de, pt, pl). If you referenced HERO.badge directly, update your component to use t('hero.badge').
- ANNOUNCEMENT.text and ANNOUNCEMENT.linkLabel removed from config/nav.ts — moved to i18n announcement keys. ANNOUNCEMENT now only holds linkHref and variant.
- PURCHASE.label, PURCHASE.badge, PURCHASE.cta, and PURCHASE.priceNote removed from config/features.ts — moved to i18n purchase keys. PURCHASE now only holds price and gumroadUrl.
Minorv1.1.0
SEO & Accessibility Pass
- OG images now served as PNG via a new /og/[...route].png route (better Twitter/X, iMessage and Slack support). Shared builder module extracted to src/lib/og-builder.ts.
- Blog posts and docs pages now use their own per-page OG card instead of the generic home image.
- preconnect and dns-prefetch hints added to <head> for every configured external service (GA4, Plausible, PostHog, Stripe, Crisp).
- Breadcrumbs auto-generated from the URL path on any route with 2+ segments — no manual wiring needed for blog posts and docs.
- Status page (/status, /es/status) now computes overallStatus from the services content collection at build time instead of being hardcoded to 'operational'.
- Mobile drawer aria-hidden toggled on open/close so screen readers track the drawer's visibility state correctly.
- scroll-behavior: auto added to the prefers-reduced-motion media query, suppressing smooth scrolling for users with vestibular sensitivity.
- demoDropdown feature flag now defaults to false — the demo dropdown is hidden in production without any extra configuration.
- ESLint: 0 errors, 0 warnings across the entire codebase (5 errors and 5 warnings resolved).
Majorv1.0.0
Initial Public Release
- Config split into 6 modular files (site.ts, nav.ts, hero.ts, integrations.ts, pricing.ts, features.ts) — build time dropped from 4.35s to 3.11s.
- SiteFooter and MobileDrawer extracted from Layout.astro — main layout shrunk from 554 to 322 lines.
- 68 tests added with Vitest covering middleware, API helpers, and i18n routing. Zero test failures.
- ESLint configured with astro-eslint-parser — 0 errors, 0 warnings on the full codebase.
- API routes implemented: /api/waitlist, /api/contact, /api/auth/login, /api/auth/signup, /api/auth/forgot-password.
- Honeypot anti-bot field on all 5 forms, validated server-side on every route.
- Rate limiting: sliding-window limiter (5 req/10 min per IP) on all API routes.
- Content Security Policy built dynamically from configured integrations, plus X-Frame-Options, HSTS, Referrer-Policy, and Permissions-Policy headers.
- Skip-to-content link for keyboard and screen reader users.
- aria-current='page' on active nav links with visual highlight.
- View Transitions and animations disabled when prefers-reduced-motion is set.
- Mobile drawer focus trap using the inert attribute — focus moves to close button on open.
- JSON-LD schemas: WebSite + Organization on all pages, FAQPage + Offer on /pricing, Article on blog posts.
- robots.txt updated to disallow all /api/ routes from crawling.
- README completely rewritten with feature table, config breakdown, API reference, integrations guide, and deployment instructions.
Minorv0.9.1-beta
Private Beta
- Full EN + ES i18n with automatic locale detection, hreflang for 12 locales, and fallback to English for missing keys.
- OG image generation with Satori and content-hash caching — ~24 cards generated at build time.
- llms.txt and llms-full.txt endpoints for LLM discoverability, generated dynamically from content collections.
- ThemePicker with 7 accent presets, free color picker, WCAG contrast checker, and CSS export. Persists to localStorage.
- Maintenance mode and waitlist mode feature flags in middleware with per-path exemptions.
- 14 documentation pages, 6 blog posts, and full content collections for testimonials, roadmap, changelog, team, services, and metrics.
- Astro View Transitions with transition:persist on ThemePicker, PurchaseBanner, and CookieBanner.
- 20+ reusable UI primitives: Button, Badge, Card, Input, Textarea, Select, Switch, Accordion, Tabs, Tooltip, and more.
Majorv0.9.0-beta
Template Foundation
- Astro 6 + Tailwind v4 base with CSS-first configuration — no tailwind.config.js needed.
- Dark-first design system with 8 CSS custom-property tokens, 7 accent presets, and a floating ThemePicker.
- 50+ pages: homepage, pricing, about, customers, open metrics, status, roadmap, contact, blog, docs, changelog, dashboard shell, auth forms, and legal.
- 90+ Astro components organized by feature area (auth, blog, changelog, dashboard, home, pricing, roadmap, status, ui).
- AppLayout and Layout shells — marketing pages with nav/footer, app pages with bare chrome.
- Strict TypeScript throughout with Astro's strict tsconfig preset.
- Self-hosted Inter font via @fontsource — no Google Fonts CDN call.
- Phosphor Icons integration with a curated 28-icon bundle to minimize JS weight.
Majorv0.8.0-alpha
Alpha Scaffold
- Project scaffold: Astro 6, Tailwind v4, TypeScript strict mode, Vitest, ESLint.
- Core layout shell with sticky nav, footer, and mobile drawer.
- Homepage with hero section, feature grid, pricing cards, testimonials, and changelog preview.
- Dashboard shell: sidebar, header, overview stats, billing, users, settings, and logs pages.
- Auth pages: login, signup, forgot-password — full UI with accessible forms.
- Content collections defined for blog, docs, roadmap, testimonials, changelog, team, services, stack, metrics, FAQ, and case studies.
- i18n routing structure established for EN (default) and ES (/es/ prefix).
- CSS design tokens: void/deep/surface/elevated/subtle/muted palette, text hierarchy, accent system.