Custom Elements architecture
- Web Components: I chose native Custom Elements over Angular/React/Vue because a small amount of code could accomplish the interactive goals without framework overhead.
- Career Experience Component: I implemented progressive enhancement that provides static dates in HTML with dynamic updates.
- Scroll Navigation Component: Migrated from jQuery scroll events to Intersection Observer for modern API patterns.
- Intersection Observer Configuration:
Used
rootMargin: '-20% 0px -80%'which counterintuitively results a 1-pixel height observation area that ensures only one section can haveisIntersecting: trueat any time, preventing multiple navigation items from activating simultaneously. The -20% top margin positions the observation area about one-fifth down the viewport for smoother navigation transitions. - DOM Encapsulation: Custom Elements enable accessing navigation elements within component tags without requiring global IDs or unique classes.
const options: IntersectionObserverInit = {
// This makes the scroll area root a single line 20% down from the top of the viewport.
// Which makes it so only a single section enabled at once.
rootMargin: '-20% 0px -80%', // Unlike CSS these must be 0px
threshold: 0,
}; For a static website with minimal interactive requirements, JavaScript frameworks introduce unnecessary complexity and bundle size. Native Custom Elements work well enough while keeping the DOM and logic co-located, improving maintainability and reducing the complexity of managing framework-specific patterns for simple interactions.
The career experience component uses progressive enhancement by serving static calculation in the initial HTML for non-JavaScript environments while automatically updating to current calculations when JavaScript executes. This approach ensures the content remains functional and informative regardless of JavaScript availability while providing enhanced accuracy when possible.
The scroll navigation feature existed in the previous Parcel-based website using jQuery scroll events. While scroll events weren’t actually problematic for this simple single-page site, migrating to Intersection Observer provided better performance characteristics and follows current practices. Typical Intersection Observer implementations for this type of feature watch for heading elements, but mine triggers on any part of the section content instead.
Configuring the Intersection Observer required understanding that rootMargin syntax differs from
CSS margin properties, where negative percentages shrink the root intersection area rather than expanding it. The fundamental problem with default Intersection Observer behavior for this case is that multiple sections can intersect simultaneously, making it impractical to identify which section should actually activate the corresponding navigation item. The solution ensures that only one section intersects at any time.
Progressive Web App integration
- Motivation: HTML5 Boilerplate
includes a default
site.webmanifestfile, and research showed PWA features are essentially free to implement with Vite and Workbox integration. - Implementation:
I used
@vite-pwa/astro with separate
webmanifest.jsonto keep static configuration outside build files. - Performance Benefits: Resource precaching improves loading times for multi-page navigation, though not critical for the original single-page architecture.
- Configuration Challenges: Required careful testing of build artifacts to verify the precache list included necessary resources while excluding unnecessary ones.
- Integration Requirements:
Resolved
TypeScript
recognition by adding references to
env.d.tsand configured script import in an EndScripts component.
The decision to implement PWA features came from learning that many developers consider them essentially free additions when using modern build tools. Since HTML5 Boilerplate had already included a manifest file that went unused, researching PWA implementation revealed minimal effort required for performance improvements.
The vite-pwa examples demonstrate practically every available option but lack clear explanations of individual option purposes. Understanding the configuration impact required checking both Vite PWA documentation and official Workbox docs alongside example configurations.
While PWA features aren’t required for basic static websites, they represent necessary infrastructure for modern web applications. Implementing them on a simple site provides familiarity with the tooling and patterns that will be essential for more complex projects requiring offline capabilities and enhanced performance characteristics.
Content Security Policy implementation
- Security Analysis Compliance: Implemented CSP with SRI hashes based on recommendations from Mozilla Observatory security analysis tools.
- Static Site Strategy: Chose SRI hashes over nonces because server-side code generation isn’t available for static sites, making SRI the most secure alternative approach.
- Implementation Tool: Used @kindspells/astro-shield for automatic SRI hash generation and integration with build process.
- Asset Inlining Decision:
Disabled resource inlining (
assetsInlineLimit: 0) based on security analysis recommendations, accepting minimal performance cost for security boundary benefits. - Current Compromise:
Using
script-src 'self'as the only relaxed rule, with plans to evaluate removing'self'directive in future iterations.
Security analysis tools and Angular’s improved support motivated the CSP implementation. Rather than implementing relaxed rules that provide minimal security benefits, the strict approach with SRI hashes provides meaningful protection against code injection attacks, even for static sites with limited server-side functionality.
SRI hash integration was the primary technical challenge, specifically how to inject dynamically generated hashes into HTTP response headers managed by AWS Amplify. The @kindspells/astro-shield tool handles hash computation effectively, but requires custom scripting to export hashes in JSON format for Terraform consumption, as detailed in the deployment process.
Disabling asset inlining eliminates potential security vulnerabilities where inline resources could bypass CSP restrictions. The performance cost of additional HTTP requests proved negligible compared to the security boundary benefits, particularly when pursuing comprehensive security analysis tool compliance. The goal of achieving clean security reports from analysis tools justified the trade-off between minimal performance costs and enhanced security posture.
Future improvements will explore
Astro’s experimental CSP features
currently in beta and evaluate whether the 'self' directive can be removed entirely while maintaining functionality. The current CSP configuration provides a solid foundation for expansion as the site grows in complexity.
Development environment
- Editor Choice: Configured Sublime Text with linting and LSP packages for code quality enforcement during development.
- Configuration Reference: Sublime Text configuration available at GitHub User Package repository for interested developers.
While most developers use VSCode, Sublime Text remains a viable option with proper LSP and linting integration. The configuration provides real-time feedback for TypeScript, ESLint, and Stylelint rules, ensuring code quality standards are maintained throughout the development process. Since this represents a minority editor choice, detailed configuration explanation would benefit few readers compared to linking to the complete setup for those specifically interested in Sublime Text workflows.
Astro development experience
- TypeScript Integration: Astro’s native TypeScript support provides excellent developer experience without additional configuration complexity.
- Build Process Coordination: Tool integration requires attention to execution order, ensuring SRI hash computation occurs after compression and PWA generation.
- Development Server: Vite-powered development server provides fast hot reload and developer experience improvements.
- Astro Islands Limitation: Islands architecture only supports SPA frameworks like React and Vue, not native Custom Elements integration.
Astro’s TypeScript-first approach eliminates the configuration overhead typically associated with adding TypeScript to JavaScript frameworks. The build process integration between multiple tools requires minimal coordination beyond ensuring SRI hash computation happens after all asset optimization and PWA manifest generation completes.
The Vite development server provides better performance than previous build tool experiences, explaining why frameworks like Angular are migrating to Vite-based development servers. The hot reload functionality works reliably with the TypeScript and Custom Elements combination without requiring special configuration.
While Astro Islands provide powerful partial hydration capabilities for framework components, the lack of direct Custom Elements support represents a missed opportunity for developers preferring native web technologies over framework dependencies. This limitation doesn’t affect functionality but prevents leveraging Astro’s selective hydration optimizations for Custom Elements.
Technical decision framework
- Tool Selection Criteria: Prioritize identifying the correct tool for specific job requirements rather than using familiar tools regardless of fit.
- Framework vs Native Choice: Astro and Custom Elements proved correct for static site requirements compared to React and SSR alternatives.
- Security vs Performance Trade-offs: Default to security when faced with trade-offs, trusting that performance improvements occur over time while security typically degrades without active maintenance.
- Optimal Strategy: Pursue both security and performance when possible, accepting trade-offs only when genuine conflicts exist between requirements.
| Consideration | SPA Frameworks | Custom Elements |
|---|---|---|
| Bundle size | 30–100+ KB | ~2 KB |
| Learning curve | Framework-specific patterns | Web standards |
| State management | Built-in or required library | Manual (simpler for small scope) |
| Best for | Complex interactive apps | Progressive enhancement |
The framework selection process shows why of matching tools to specific requirements rather than defaulting to popular choices. For a static site with minimal interactivity, Custom Elements works well enough without the overhead and complexity of larger frameworks designed for complex application state management.
The security-first approach when facing trade-offs reflects the reality that performance optimizations continue improving through tool updates and hardware advancement, while security posture tends to degrade over time without active maintenance. This perspective influenced decisions like disabling asset inlining and implementing strict CSP policies despite minimal performance costs.
When genuine conflicts between security and performance requirements don’t exist, pursuing both objectives simultaneously produces superior outcomes. The PWA implementation demonstrates this approach by providing both security benefits through manifest configuration and performance improvements through resource precaching without compromising either objective.