Jelajahi Sumber

Brand: cycle logo in the page header + SVG favicon

Drops doc/logo-3-cycle.svg into public/favicon.svg so Apache serves it
at /favicon.svg directly (DocumentRoot is public/, FallbackResource
sends only unknown paths to index.php). Both layouts gain a
<link rel="icon" type="image/svg+xml" href="/favicon.svg"> in <head>;
the standalone .svg already carries its own svg:root +
prefers-color-scheme block, so the favicon adapts to the OS theme on
its own.

The main header anchor (<a href="/">) now flexes the cycle mark next
to the "Sprint Planner" wordmark. The mark is inlined with no <style>
block — strict CSP is style-src 'self' (no 'unsafe-inline') — and
relies on currentColor inheriting from the link, so the strokes and
phase dots track the existing light/dark text colour. The radial
glow's id is namespaced ("brand-cycle-glow") to avoid colliding with
anything else on the page.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chiappa 2 hari lalu
induk
melakukan
90b10d5342
3 mengubah file dengan 45 tambahan dan 1 penghapusan
  1. 22 0
      public/favicon.svg
  2. 1 0
      views/layout-bare.twig
  3. 22 1
      views/layout.twig

+ 22 - 0
public/favicon.svg

@@ -0,0 +1,22 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="none" role="img" aria-label="Sprint Planner — Sprint Cycle">
+  <style>
+    svg:root { color: #0f172a; }
+    @media (prefers-color-scheme: dark) { svg:root { color: #e2e8f0; } }
+  </style>
+  <defs>
+    <radialGradient id="cycle-glow" cx="32" cy="32" r="20" gradientUnits="userSpaceOnUse">
+      <stop offset="0"   stop-color="#6366f1" stop-opacity="0.55"/>
+      <stop offset="0.6" stop-color="#6366f1" stop-opacity="0.12"/>
+      <stop offset="1"   stop-color="#6366f1" stop-opacity="0"/>
+    </radialGradient>
+  </defs>
+  <circle cx="32" cy="32" r="20" fill="url(#cycle-glow)"/>
+  <path d="M52 32 A20 20 0 1 1 32 12" stroke="currentColor" stroke-width="3.5" stroke-linecap="round"/>
+  <path d="M44 8 L52 12 L48 20" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
+  <circle cx="32" cy="32" r="5" fill="#6366f1"/>
+  <circle cx="48" cy="20" r="2.5" fill="currentColor" opacity="0.55"/>
+  <circle cx="52" cy="40" r="2.5" fill="currentColor" opacity="0.55"/>
+  <circle cx="40" cy="50" r="2.5" fill="currentColor" opacity="0.55"/>
+  <circle cx="20" cy="48" r="2.5" fill="currentColor" opacity="0.55"/>
+  <circle cx="14" cy="32" r="2.5" fill="#10b981"/>
+</svg>

+ 1 - 0
views/layout-bare.twig

@@ -4,6 +4,7 @@
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width,initial-scale=1">
     <title>{{ title|default(sprint.name ~ ' — present') }}</title>
+    <link rel="icon" type="image/svg+xml" href="/favicon.svg">
     <script src="/assets/js/theme-init.js"></script>
     <link rel="stylesheet" href="/assets/css/app.css">
     <script src="/assets/js/vendor/sortable.min.js" defer></script>

+ 22 - 1
views/layout.twig

@@ -4,6 +4,7 @@
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width,initial-scale=1">
     <title>{{ title|default('Sprint Planner') }}</title>
+    <link rel="icon" type="image/svg+xml" href="/favicon.svg">
     <script src="/assets/js/theme-init.js"></script>
     <link rel="stylesheet" href="/assets/css/app.css">
     <script src="/assets/js/vendor/htmx.min.js" defer></script>
@@ -15,7 +16,27 @@
 <body class="bg-slate-100 text-slate-900 antialiased dark:bg-slate-900 dark:text-slate-100">
     <header class="border-b bg-white dark:bg-slate-800 dark:border-slate-700">
         <div class="max-w-7xl mx-auto px-4 py-3 flex items-center gap-4">
-            <a href="/" class="font-semibold tracking-tight">Sprint Planner</a>
+            <a href="/" class="flex items-center gap-2 font-semibold tracking-tight">
+                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="24" height="24" aria-hidden="true" class="block" fill="none">
+                    <defs>
+                        <radialGradient id="brand-cycle-glow" cx="32" cy="32" r="20" gradientUnits="userSpaceOnUse">
+                            <stop offset="0"   stop-color="#6366f1" stop-opacity="0.55"/>
+                            <stop offset="0.6" stop-color="#6366f1" stop-opacity="0.12"/>
+                            <stop offset="1"   stop-color="#6366f1" stop-opacity="0"/>
+                        </radialGradient>
+                    </defs>
+                    <circle cx="32" cy="32" r="20" fill="url(#brand-cycle-glow)"/>
+                    <path d="M52 32 A20 20 0 1 1 32 12" stroke="currentColor" stroke-width="3.5" stroke-linecap="round"/>
+                    <path d="M44 8 L52 12 L48 20" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"/>
+                    <circle cx="32" cy="32" r="5"  fill="#6366f1"/>
+                    <circle cx="48" cy="20" r="2.5" fill="currentColor" opacity="0.55"/>
+                    <circle cx="52" cy="40" r="2.5" fill="currentColor" opacity="0.55"/>
+                    <circle cx="40" cy="50" r="2.5" fill="currentColor" opacity="0.55"/>
+                    <circle cx="20" cy="48" r="2.5" fill="currentColor" opacity="0.55"/>
+                    <circle cx="14" cy="32" r="2.5" fill="#10b981"/>
+                </svg>
+                <span>Sprint Planner</span>
+            </a>
 
             <nav class="ml-auto flex items-center gap-4 text-sm">
                 {% if currentUser is not null %}