SPB Git

spb/svgarden Public

SVGarden — searchable bank of 74 self-contained SVG+CSS animation snippets (svgarden.dev)

HTML 79.2% Astro 10.3% JavaScript 6% CSS 3.7% Shell 0.8%
4.1 KB · 96 lines astro
Raw Blame History
1---2/**3 * ============================================================4 * SVGarden — https://www.svgarden.dev5 * Author : Simon-Pierre Boucher6 * Contact: contact@spboucher.ai7 * File   : src/layouts/Base.astro8 * Desc   : Base layout — head, theme init, header nav, footer9 * ============================================================10 */11import '../styles/global.css';1213interface Props {14  title?: string;15  description?: string;16}1718const {19  title = 'SVGarden — SVG + CSS animation snippets',20  description = 'A searchable garden of self-contained SVG + CSS animations. Preview, customize, copy — every snippet works in a blank HTML file.',21} = Astro.props;2223const canonical = new URL(Astro.url.pathname, Astro.site);24const current = Astro.url.pathname;25---2627<!DOCTYPE html>28<html lang="en">29  <head>30    <meta charset="utf-8" />31    <meta name="viewport" content="width=device-width, initial-scale=1" />32    <title>{title}</title>33    <meta name="description" content={description} />34    <meta name="author" content="Simon-Pierre Boucher" />35    <link rel="canonical" href={canonical} />36    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />37    <meta property="og:title" content={title} />38    <meta property="og:description" content={description} />39    <meta property="og:type" content="website" />40    <meta property="og:url" content={canonical} />41    <meta property="og:site_name" content="SVGarden" />42    <script is:inline>43      // Theme init — runs before paint to avoid a flash of the wrong theme.44      (() => {45        const stored = localStorage.getItem('sg-theme');46        const theme = stored ?? (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');47        document.documentElement.dataset.theme = theme;48      })();49    </script>50  </head>51  <body>52    <a class="sg-skip-link" href="#sg-main">Skip to content</a>53    <header class="sg-header">54      <div class="sg-container sg-header-inner">55        <a class="sg-logo" href="/" aria-label="SVGarden home">56          <svg width="26" height="26" viewBox="0 0 32 32" aria-hidden="true">57            <path d="M16 28 V14" stroke="var(--sg-accent)" stroke-width="2.5" stroke-linecap="round" fill="none" />58            <path d="M16 18 C 16 12, 10 12, 7 7 C 14 7, 17 11, 16 18 Z" fill="var(--sg-accent)" />59            <path d="M16 22 C 16 17, 21 17, 25 12 C 18 12, 15 16, 16 22 Z" fill="var(--sg-accent)" opacity="0.55" />60          </svg>61          SVGarden62        </a>63        <nav class="sg-nav" aria-label="Main">64          <a href="/" aria-current={current === '/' ? 'page' : undefined}>Gallery</a>65          <a href="/about" aria-current={current === '/about' ? 'page' : undefined}>About</a>66          <button class="sg-theme-toggle" type="button" aria-label="Toggle dark mode" data-sg-theme-toggle>67            <svg class="sg-icon-moon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true">68              <path d="M21 12.8A9 9 0 1 1 11.2 3 7 7 0 0 0 21 12.8Z" />69            </svg>70            <svg class="sg-icon-sun" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true">71              <circle cx="12" cy="12" r="4" />72              <path d="M12 2v2m0 16v2M4.9 4.9l1.4 1.4m11.4 11.4 1.4 1.4M2 12h2m16 0h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4" />73            </svg>74          </button>75        </nav>76      </div>77    </header>78    <main id="sg-main" class="sg-container">79      <slot />80    </main>81    <footer class="sg-footer">82      <div class="sg-container">83        © Simon-Pierre Boucher — <a href="mailto:contact@spboucher.ai">contact@spboucher.ai</a> — <a href="https://www.svgarden.dev">svgarden.dev</a>84      </div>85    </footer>86    <script>87      const toggle = document.querySelector('[data-sg-theme-toggle]');88      toggle?.addEventListener('click', () => {89        const next = document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark';90        document.documentElement.dataset.theme = next;91        localStorage.setItem('sg-theme', next);92      });93    </script>94  </body>95</html>96