The /pattern-graph-paper.svg public asset was 404ing in production, so the hero grid pattern never rendered. Inline the SVG as a data URI in the mask so it can never miss. While here, turn the "$ npx executor web" hero link into a button that copies the command to the clipboard, with a copy icon that crossfades to a check on success.
Unified · Split
| @@ -43,10 +43,14 @@ import { AnimatedBeamDemo } from '../components/animated-beam-demo' · 42 unmodified lines | ||
| 43 | 43 | </p> |
| 44 | 44 | <div class="rise-4 flex flex-wrap items-center justify-center gap-5"> |
| 45 | 45 | <a href="/cloud" class="btn-primary">Try Cloud →</a> |
| 46 | − <a href="#install" class="btn-link"> | |
| 46 | + <button type="button" class="btn-link copy-btn" data-copy="npx executor web" aria-label="Copy npx executor web to clipboard"> | |
| 47 | 47 | <span class="font-mono text-[13px] text-ink-3">$</span> |
| 48 | 48 | <span>npx executor web</span> |
| 49 | − </a> | |
| 49 | + <span class="copy-icons text-ink-3" aria-hidden="true"> | |
| 50 | + <svg class="copy-icon-copy" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg> | |
| 51 | + <svg class="copy-icon-check" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg> | |
| 52 | + </span> | |
| 53 | + </button> | |
| 50 | 54 | </div> |
| 51 | 55 | </div> |
| 52 | 56 | </div> |
| @@ -291,3 +295,23 @@ import { AnimatedBeamDemo } from '../components/animated-beam-demo' · 238 unmodified lines | ||
| 291 | 295 | </div> |
| 292 | 296 | </main> |
| 293 | 297 | </Layout> |
| 298 | + | |
| 299 | +<script> | |
| 300 | + document.querySelectorAll<HTMLButtonElement>('button[data-copy]').forEach((btn) => { | |
| 301 | + let timer: ReturnType<typeof setTimeout> | null = null | |
| 302 | + | |
| 303 | + btn.addEventListener('click', async () => { | |
| 304 | + const text = btn.dataset.copy ?? '' | |
| 305 | + try { | |
| 306 | + await navigator.clipboard.writeText(text) | |
| 307 | + btn.dataset.copied = 'true' | |
| 308 | + } catch { | |
| 309 | + btn.dataset.copied = 'false' | |
| 310 | + } | |
| 311 | + if (timer) clearTimeout(timer) | |
| 312 | + timer = setTimeout(() => { | |
| 313 | + delete btn.dataset.copied | |
| 314 | + }, 1500) | |
| 315 | + }) | |
| 316 | + }) | |
| 317 | +</script> | |
| @@ -55,8 +55,9 @@ body { · 54 unmodified lines | ||
| 55 | 55 | z-index: 1; |
| 56 | 56 | } |
| 57 | 57 | .pattern-graph { |
| 58 | − -webkit-mask-image: url("/pattern-graph-paper.svg"); | |
| 59 | − mask-image: url("/pattern-graph-paper.svg"); | |
| 58 | + --pattern-graph: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='100'%20height='100'%20viewBox='0%200%20100%20100'%3E%3Cg%20fill-rule='evenodd'%3E%3Cg%20fill='%23000'%3E%3Cpath%20opacity='.5'%20d='M96%2095h4v1h-4v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9zm-1%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-9-10h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm9-10v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-9-10h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm9-10v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-9-10h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm9-10v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-10%200v-9h-9v9h9zm-9-10h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9zm10%200h9v-9h-9v9z'/%3E%3Cpath%20d='M6%205V0H5v5H0v1h5v94h1V6h94V5H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); | |
| 59 | + -webkit-mask-image: var(--pattern-graph); | |
| 60 | + mask-image: var(--pattern-graph); | |
| 60 | 61 | -webkit-mask-size: 100px 100px; |
| 61 | 62 | mask-size: 100px 100px; |
| 62 | 63 | opacity: 0.08; |
| @@ -141,11 +142,42 @@ body { · 78 unmodified lines | ||
| 141 | 142 | align-items: center; |
| 142 | 143 | gap: 0.4rem; |
| 143 | 144 | transition: color 0.15s ease; |
| 145 | + background: transparent; | |
| 146 | + border: 0; | |
| 147 | + padding: 0; | |
| 148 | + font-family: inherit; | |
| 149 | + cursor: pointer; | |
| 144 | 150 | } |
| 145 | 151 | .btn-link:hover { |
| 146 | 152 | color: var(--color-ink); |
| 147 | 153 | } |
| 148 | 154 | |
| 155 | +.copy-icons { | |
| 156 | + position: relative; | |
| 157 | + display: inline-flex; | |
| 158 | + width: 14px; | |
| 159 | + height: 14px; | |
| 160 | + margin-left: 0.15rem; | |
| 161 | +} | |
| 162 | +.copy-icons > svg { | |
| 163 | + position: absolute; | |
| 164 | + inset: 0; | |
| 165 | + transition: opacity 0.15s ease, transform 0.15s ease; | |
| 166 | +} | |
| 167 | +.copy-icon-check { | |
| 168 | + opacity: 0; | |
| 169 | + transform: scale(0.8); | |
| 170 | + color: var(--color-accent); | |
| 171 | +} | |
| 172 | +.copy-btn[data-copied="true"] .copy-icon-copy { | |
| 173 | + opacity: 0; | |
| 174 | + transform: scale(0.8); | |
| 175 | +} | |
| 176 | +.copy-btn[data-copied="true"] .copy-icon-check { | |
| 177 | + opacity: 1; | |
| 178 | + transform: scale(1); | |
| 179 | +} | |
| 180 | + | |
| 149 | 181 | /* Legal pages */ |
| 150 | 182 | .legal-prose { |
| 151 | 183 | color: color-mix(in srgb, var(--color-ink) 85%, transparent); |