From 5c0ab4ee502d7de9dc985d1e3bc794362989830c Mon Sep 17 00:00:00 2001 From: Lota Anidi Date: Sat, 13 Jun 2026 02:23:17 +0100 Subject: [PATCH 01/20] fix: trim excess page bottom padding below footer (120px -> 40px) --- site/app/globals.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/site/app/globals.css b/site/app/globals.css index f5dc1e9..659109a 100644 --- a/site/app/globals.css +++ b/site/app/globals.css @@ -41,12 +41,13 @@ body::before { height: 3px; background: linear-gradient(90deg, #295df6, #c6a0fd, #5cd500, #ff7a45, #ff3e8c, #00c9a7); z-index: 100; + backdrop-filter: blur(100px); } main { max-width: var(--maxw); margin: 0 auto; - padding: 0 20px 120px; + padding: 0 20px 40px; } a { color: var(--accent); text-decoration: none; } From eec4a9c8bb719024db05bd09eff674b2a867d24f Mon Sep 17 00:00:00 2001 From: Lota Anidi Date: Sat, 13 Jun 2026 02:27:59 +0100 Subject: [PATCH 02/20] fix: derive footer 'Last updated' + copyright year at build time (no hardcoded date) --- site/components/Footer.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/site/components/Footer.tsx b/site/components/Footer.tsx index 6667d1e..549729f 100644 --- a/site/components/Footer.tsx +++ b/site/components/Footer.tsx @@ -1,4 +1,15 @@ export function Footer() { + // Server component → these evaluate at build time, so "Last updated" reflects + // the most recent deploy and auto-advances on every build. + const now = new Date() + const year = now.getFullYear() + const lastUpdated = now.toLocaleDateString('en-US', { + month: 'long', + day: 'numeric', + year: 'numeric', + timeZone: 'UTC', + }) + return (
- © 2026 Made by Lota - Last updated: June 13, 2026 + © {year} Made by Lota + Last updated: {lastUpdated}
From bec6297da9beb44d6495296cfd8e6453a52c63ef Mon Sep 17 00:00:00 2001 From: Lota Anidi Date: Sat, 13 Jun 2026 02:46:27 +0100 Subject: [PATCH 03/20] fix: vertically center Copy button in the single-line Install box --- site/app/globals.css | 14 ++++++++++++-- site/components/CopyButton.tsx | 8 +++++--- site/components/InstallTabs.tsx | 2 +- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/site/app/globals.css b/site/app/globals.css index 659109a..3fea7c9 100644 --- a/site/app/globals.css +++ b/site/app/globals.css @@ -41,13 +41,13 @@ body::before { height: 3px; background: linear-gradient(90deg, #295df6, #c6a0fd, #5cd500, #ff7a45, #ff3e8c, #00c9a7); z-index: 100; - backdrop-filter: blur(100px); + } main { max-width: var(--maxw); margin: 0 auto; - padding: 0 20px 40px; + padding: 0 20px 30px; } a { color: var(--accent); text-decoration: none; } @@ -134,3 +134,13 @@ a { transition: opacity 0.16s ease, color 0.18s ease; } } @keyframes ss-rise { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } } @keyframes ss-fade { from { opacity: 0; } to { opacity: 1; } } + +/* Vertically-centered Copy button (single-line code boxes like Install). + Transforms compose with the centering so hover/press don't reset it. */ +.copy-center { top: 50%; transform: translateY(-50%); } +@media (prefers-reduced-motion: no-preference) { + .copy-center:active { transform: translateY(-50%) scale(0.97); } +} +@media (prefers-reduced-motion: no-preference) and (hover: hover) and (pointer: fine) { + .copy-center:hover { transform: translateY(-50%) translateY(-1px); } +} diff --git a/site/components/CopyButton.tsx b/site/components/CopyButton.tsx index 5691d54..26f6fa4 100644 --- a/site/components/CopyButton.tsx +++ b/site/components/CopyButton.tsx @@ -1,11 +1,11 @@ 'use client' import { useState } from 'react' -export function CopyButton({ text }: { text: string }) { +export function CopyButton({ text, center = false }: { text: string; center?: boolean }) { const [copied, setCopied] = useState(false) return ( ) } From 86fd9a6924a128a7e5ae45402476804b9d12bf81 Mon Sep 17 00:00:00 2001 From: Lota Anidi Date: Sat, 13 Jun 2026 02:57:23 +0100 Subject: [PATCH 05/20] style: make --accent a dark neutral (kill blue accent text) --- site/app/globals.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/app/globals.css b/site/app/globals.css index b7dc7da..7a231f9 100644 --- a/site/app/globals.css +++ b/site/app/globals.css @@ -4,7 +4,7 @@ --text: #333333; --muted: #8a8a8a; --border: #ececec; - --accent: #295df6; + --accent: #1a1a1a; /* dark neutral — no blue accent text */ /* The `, system-ui` fallback inside var() matters: if the next/font variable isn't resolved yet, var() would otherwise invalidate font-family → serif. */ From 6386bbd6b734b250630733d66d6d30a3b3c2ea31 Mon Sep 17 00:00:00 2001 From: Lota Anidi Date: Sat, 13 Jun 2026 03:30:57 +0100 Subject: [PATCH 06/20] =?UTF-8?q?feat:=20add=20'Prompt'=20tab=20to=20Insta?= =?UTF-8?q?ll=20=E2=80=94=20copy-this-to-your-agent=20setup=20prompt=20(re?= =?UTF-8?q?act-grab=20style)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/components/InstallTabs.tsx | 60 +++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/site/components/InstallTabs.tsx b/site/components/InstallTabs.tsx index e4925f1..0f60d54 100644 --- a/site/components/InstallTabs.tsx +++ b/site/components/InstallTabs.tsx @@ -9,16 +9,34 @@ const COMMANDS: Record = { bun: 'bun add stampstack', } +// Copy-this-to-your-agent prompt (react-grab style). +const PROMPT = `Set up stampstack in this React project. + +1. Detect the package manager from the lockfile and install stampstack (e.g. npm i stampstack). +2. Where you use it, import the component and its styles: + import { StampStack } from 'stampstack' + import 'stampstack/styles.css' +3. Render it with your data — each item only needs an id: +
{item.title}
} /> +4. Optional: frameColor={(item) => '#...'} for a per-stamp frame color, and + onSelect={(item) => ...} to open a card on tap. + +It renders real DOM (not a canvas), so any content — text, images, links — works inside renderStamp.` + +const TABS = ['npm', 'pnpm', 'yarn', 'bun', 'Prompt'] as const + export function InstallTabs() { - const [pm, setPm] = useState('npm') + const [tab, setTab] = useState<(typeof TABS)[number]>('npm') + const isPrompt = tab === 'Prompt' + return (
- {Object.keys(COMMANDS).map((key) => ( + {TABS.map((key) => ( ))}
-
-
-          $ {COMMANDS[pm]}
-        
- -
+ + {isPrompt ? ( +
+

+ {PROMPT} +

+ +
+ ) : ( +
+
+            $ {COMMANDS[tab]}
+          
+ +
+ )}
) } From 4a8b54ca4e1043fd42d25b07b6802147dae080a6 Mon Sep 17 00:00:00 2001 From: Lota Anidi Date: Sat, 13 Jun 2026 17:20:28 +0100 Subject: [PATCH 07/20] =?UTF-8?q?Tighten=20hero=E2=86=92Install=20spacing?= =?UTF-8?q?=20(Section=20marginTop=2072=E2=86=9232)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/app/page.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/site/app/page.tsx b/site/app/page.tsx index 992f7ea..45abb53 100644 --- a/site/app/page.tsx +++ b/site/app/page.tsx @@ -2,6 +2,7 @@ import { Hero } from '@/components/Hero' import { Section } from '@/components/Section' import { Reveal } from '@/components/Reveal' import { InstallTabs } from '@/components/InstallTabs' +import { ActionButtons } from '@/components/ActionButtons' import { CodeBlock } from '@/components/CodeBlock' import { Footer } from '@/components/Footer' @@ -27,8 +28,9 @@ export default function Home() { -
+
+
From c053e4b898f17be178d1dbdc93fe2f7af5522519 Mon Sep 17 00:00:00 2001 From: Lota Anidi Date: Sat, 13 Jun 2026 17:41:34 +0100 Subject: [PATCH 08/20] Wrap code box content instead of horizontal scroll --- site/app/globals.css | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/site/app/globals.css b/site/app/globals.css index 7a231f9..dcbcc7d 100644 --- a/site/app/globals.css +++ b/site/app/globals.css @@ -13,7 +13,7 @@ Cascadia first (modern, if installed), Consolas as the guaranteed catch-all. */ --font-mono: "SF Mono", SFMono-Regular, ui-monospace, "Cascadia Code", "Cascadia Mono", Consolas, "Courier New", monospace; - --maxw: 640px; + --maxw: 560px; } * { box-sizing: border-box; } @@ -33,15 +33,20 @@ html, body { overflow-x: clip; } -/* A thin gradient hairline along the very top — the one playful accent. */ +/* A soft, blurred rainbow wash across the very top — the one playful accent. + Figma treatment (node 1787:9551): a ~13px band heavily blurred (33px) and + dropped to 20% opacity, so the brand rainbow blooms into a pastel glow. + pointer-events:none so the bleed never blocks clicks on the header below. */ body::before { content: ''; position: fixed; top: 0; left: 0; right: 0; - height: 3px; + height: 14px; background: linear-gradient(90deg, #295df6, #c6a0fd, #5cd500, #ff7a45, #ff3e8c, #00c9a7); + filter: blur(33px); + opacity: 0.2; + pointer-events: none; z-index: 100; - } main { @@ -59,9 +64,9 @@ a:hover { text-decoration: underline; } /* The quiet section label — the haptics signature. */ .section-label { font-size: 13px; - font-weight: 500; + font-weight: 600; letter-spacing: -0.005em; - color: var(--muted); + color: #252525; /* match the Outro label (Footer.tsx) */ margin: 0 0 14px; } @@ -81,7 +86,11 @@ code, pre, .code-card pre, .code-card code { .code-card pre { margin: 0; padding: 16px 18px; - overflow-x: auto; + /* Reflow long lines inside the box instead of scrolling sideways: + pre-wrap keeps indentation + intentional breaks but wraps when too wide; + break-word lets a single long token (e.g. a path) break rather than overflow. */ + white-space: pre-wrap; + overflow-wrap: break-word; background: var(--card) !important; font-family: var(--font-mono); font-size: 13.5px; From f064aefa90d8c50ff315965751c56fac5ed6131a Mon Sep 17 00:00:00 2001 From: Lota Anidi Date: Sat, 13 Jun 2026 18:04:55 +0100 Subject: [PATCH 09/20] Make Copy buttons text-only (strip box: border/bg/radius/padding) --- site/components/CopyButton.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/site/components/CopyButton.tsx b/site/components/CopyButton.tsx index 5cf6a7c..24a646d 100644 --- a/site/components/CopyButton.tsx +++ b/site/components/CopyButton.tsx @@ -16,11 +16,11 @@ export function CopyButton({ text, center = false }: { text: string; center?: bo right: 10, // `center` (single-line boxes like Install) vertically centers via the // .copy-center class; otherwise pin to the top (multi-line code blocks). - ...(center ? {} : { top: 10 }), - border: '1px solid var(--border)', - background: 'var(--bg)', - borderRadius: 7, - padding: '4px 9px', + ...(center ? {} : { top: 14 }), + // Text-only: no box (border/background/radius/padding stripped). + border: 'none', + background: 'none', + padding: 0, fontSize: 12, fontFamily: 'var(--font-ui)', cursor: 'pointer', From 0a0a489d63db0ca30cfe8cae6bd2a836cb91b822 Mon Sep 17 00:00:00 2001 From: Lota Anidi Date: Sat, 13 Jun 2026 18:10:23 +0100 Subject: [PATCH 10/20] Copy button: align right padding to 18px, match inactive-tab text style (13/500) --- site/components/CopyButton.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/site/components/CopyButton.tsx b/site/components/CopyButton.tsx index 24a646d..eebbb11 100644 --- a/site/components/CopyButton.tsx +++ b/site/components/CopyButton.tsx @@ -13,7 +13,8 @@ export function CopyButton({ text, center = false }: { text: string; center?: bo }} style={{ position: 'absolute', - right: 10, + // Match the code's 18px horizontal padding so left/right inset reads even. + right: 18, // `center` (single-line boxes like Install) vertically centers via the // .copy-center class; otherwise pin to the top (multi-line code blocks). ...(center ? {} : { top: 14 }), @@ -21,7 +22,9 @@ export function CopyButton({ text, center = false }: { text: string; center?: bo border: 'none', background: 'none', padding: 0, - fontSize: 12, + // Match the inactive Install tabs: 13px / weight 500 / muted (color via .copy-btn). + fontSize: 13, + fontWeight: 500, fontFamily: 'var(--font-ui)', cursor: 'pointer', }} From 1061de99f4ad48d312c55800cc4b84906320488e Mon Sep 17 00:00:00 2001 From: Lota Anidi Date: Sat, 13 Jun 2026 18:30:54 +0100 Subject: [PATCH 11/20] Remove grey fill on secondary action buttons (transparent + border) --- site/components/ActionButtons.tsx | 42 +++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 site/components/ActionButtons.tsx diff --git a/site/components/ActionButtons.tsx b/site/components/ActionButtons.tsx new file mode 100644 index 0000000..5643abf --- /dev/null +++ b/site/components/ActionButtons.tsx @@ -0,0 +1,42 @@ +import type { CSSProperties } from 'react' + +// Button styles mirrored 1:1 from Figma (node 1704:513). +const btnBase: CSSProperties = { + display: 'inline-flex', + alignItems: 'center', + justifyContent: 'center', + gap: 9, + height: 36, + padding: '8px 16px', + borderRadius: 11, + fontFamily: 'var(--font-ui)', + fontWeight: 500, + fontSize: 14, + letterSpacing: '-0.5px', + lineHeight: '20px', + whiteSpace: 'nowrap', +} +const primaryBtn: CSSProperties = { ...btnBase, background: '#171717', color: '#fafafa', border: '1px solid #171717' } +const secondaryBtn: CSSProperties = { ...btnBase, background: 'transparent', color: '#0a0a0a', border: '1px solid #e5e5e5' } + +export function ActionButtons() { + return ( + + ) +} From efc3b585c64f92b681249da9e4f73a98881fc8ae Mon Sep 17 00:00:00 2001 From: Lota Anidi Date: Sat, 13 Jun 2026 18:35:12 +0100 Subject: [PATCH 12/20] Replace lift+shadow hover with subtle scale (Emil: flat UI, drastically reduced hover) --- site/app/globals.css | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/site/app/globals.css b/site/app/globals.css index dcbcc7d..c83c064 100644 --- a/site/app/globals.css +++ b/site/app/globals.css @@ -3,7 +3,7 @@ --card: #ffffff; --text: #333333; --muted: #8a8a8a; - --border: #ececec; + --border: #E5E5E5; --accent: #1a1a1a; /* dark neutral — no blue accent text */ /* The `, system-ui` fallback inside var() matters: if the next/font variable @@ -80,7 +80,7 @@ code, pre, .code-card pre, .code-card code { position: relative; background: var(--card); border: 1px solid var(--border); - border-radius: 10px; + border-radius: 15px; overflow: hidden; } .code-card pre { @@ -106,19 +106,19 @@ code, pre, .code-card pre, .code-card code { :root { --ease-out: cubic-bezier(0.23, 1, 0.32, 1); } /* strong ease-out */ /* Smooth state changes (color/bg swaps animate via the transition). */ -.ss-tap { transition: transform 0.14s var(--ease-out), box-shadow 0.2s ease, color 0.18s ease, background-color 0.18s ease, border-color 0.18s ease; } +.ss-tap { transition: transform 0.14s var(--ease-out), color 0.18s ease, background-color 0.18s ease, border-color 0.18s ease; } .ss-tab { transition: color 0.18s ease, opacity 0.18s ease, transform 0.14s var(--ease-out); } a { transition: opacity 0.16s ease, color 0.18s ease; } /* Press feedback — every pointer type, motion-safe. */ @media (prefers-reduced-motion: no-preference) { - .ss-tap:active { transform: scale(0.97); box-shadow: none; } + .ss-tap:active { transform: scale(0.97); } .ss-tab:active { transform: scale(0.95); } } /* Hover — only true hover devices, so taps on touch don't trigger it. */ @media (prefers-reduced-motion: no-preference) and (hover: hover) and (pointer: fine) { - .ss-tap:hover { transform: translateY(-1px); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); } + .ss-tap:hover { transform: scale(1.02); } .ss-tab:hover { opacity: 0.6; } a:hover { opacity: 0.6; } } From 2dbaaca049710f5d4785e8ac84ffb9624f68a5d3 Mon Sep 17 00:00:00 2001 From: Lota Anidi Date: Sat, 13 Jun 2026 18:41:52 +0100 Subject: [PATCH 13/20] Fix missing press depression: order :active after :hover (LVHA cascade) --- site/app/globals.css | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/site/app/globals.css b/site/app/globals.css index c83c064..ff72e2b 100644 --- a/site/app/globals.css +++ b/site/app/globals.css @@ -110,19 +110,22 @@ code, pre, .code-card pre, .code-card code { .ss-tab { transition: color 0.18s ease, opacity 0.18s ease, transform 0.14s var(--ease-out); } a { transition: opacity 0.16s ease, color 0.18s ease; } -/* Press feedback — every pointer type, motion-safe. */ -@media (prefers-reduced-motion: no-preference) { - .ss-tap:active { transform: scale(0.97); } - .ss-tab:active { transform: scale(0.95); } -} - -/* Hover — only true hover devices, so taps on touch don't trigger it. */ +/* Hover — only true hover devices, so taps on touch don't trigger it. + MUST come before :active below — equal specificity means source order wins, + so :active needs to be last to override the hover scale during a press. */ @media (prefers-reduced-motion: no-preference) and (hover: hover) and (pointer: fine) { .ss-tap:hover { transform: scale(1.02); } .ss-tab:hover { opacity: 0.6; } a:hover { opacity: 0.6; } } +/* Press feedback — every pointer type, motion-safe. Last so it wins over :hover + when the pointer is both hovering and pressing (the LVHA cascade order). */ +@media (prefers-reduced-motion: no-preference) { + .ss-tap:active { transform: scale(0.97); } + .ss-tab:active { transform: scale(0.95); } +} + /* Scroll-reveal: fade + rise into view (one-shot via IntersectionObserver). */ .reveal { opacity: 0; transform: translateY(14px); } .reveal.is-in { opacity: 1; transform: none; } From c10392cec8c4637edacd210842dca3bc3fbc44b0 Mon Sep 17 00:00:00 2001 From: Lota Anidi Date: Sat, 13 Jun 2026 18:43:54 +0100 Subject: [PATCH 14/20] Link footer 'Lota' to x.com/lottabydesign --- site/components/Footer.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/site/components/Footer.tsx b/site/components/Footer.tsx index 549729f..cf35c11 100644 --- a/site/components/Footer.tsx +++ b/site/components/Footer.tsx @@ -105,7 +105,17 @@ export function Footer() { color: '#484747', }} > - © {year} Made by Lota + + © {year} Made by{' '} + + Lota + + Last updated: {lastUpdated} From d377325b01bd3f57d4558004914e0226b6f6803e Mon Sep 17 00:00:00 2001 From: Lota Anidi Date: Sat, 13 Jun 2026 18:52:41 +0100 Subject: [PATCH 15/20] =?UTF-8?q?Hover=20darkens=20links=20+=20tabs=20(ins?= =?UTF-8?q?tead=20of=20fading);=20move=20colors=20inline=E2=86=92class=20s?= =?UTF-8?q?o=20:hover=20wins?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/app/globals.css | 12 +++++++++--- site/components/Footer.tsx | 4 ++-- site/components/InstallTabs.tsx | 3 +-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/site/app/globals.css b/site/app/globals.css index ff72e2b..cf463af 100644 --- a/site/app/globals.css +++ b/site/app/globals.css @@ -61,6 +61,10 @@ a:hover { text-decoration: underline; } /* Button-style links (Figma nav buttons) — no underline / opacity fade. */ .btn:hover { text-decoration: none; opacity: 1; } +/* Quiet inline links (footer) — sit in the surrounding grey at rest, darken on + hover via the a:hover rule. Color lives here (not inline) so :hover can win. */ +.link-quiet { color: inherit; text-decoration: underline; } + /* The quiet section label — the haptics signature. */ .section-label { font-size: 13px; @@ -107,7 +111,8 @@ code, pre, .code-card pre, .code-card code { /* Smooth state changes (color/bg swaps animate via the transition). */ .ss-tap { transition: transform 0.14s var(--ease-out), color 0.18s ease, background-color 0.18s ease, border-color 0.18s ease; } -.ss-tab { transition: color 0.18s ease, opacity 0.18s ease, transform 0.14s var(--ease-out); } +.ss-tab { color: var(--muted); transition: color 0.18s ease, opacity 0.18s ease, transform 0.14s var(--ease-out); } +.ss-tab.is-active { color: var(--text); } /* selected tab — colored via class so :hover can win */ a { transition: opacity 0.16s ease, color 0.18s ease; } /* Hover — only true hover devices, so taps on touch don't trigger it. @@ -115,8 +120,9 @@ a { transition: opacity 0.16s ease, color 0.18s ease; } so :active needs to be last to override the hover scale during a press. */ @media (prefers-reduced-motion: no-preference) and (hover: hover) and (pointer: fine) { .ss-tap:hover { transform: scale(1.02); } - .ss-tab:hover { opacity: 0.6; } - a:hover { opacity: 0.6; } + /* Darken on hover (toward the main text color), not fade — matches the Copy button. */ + .ss-tab:hover { color: var(--text); } + a:hover { color: var(--text); } } /* Press feedback — every pointer type, motion-safe. Last so it wins over :hover diff --git a/site/components/Footer.tsx b/site/components/Footer.tsx index cf35c11..ca80631 100644 --- a/site/components/Footer.tsx +++ b/site/components/Footer.tsx @@ -73,10 +73,10 @@ export function Footer() {

Drag / flick release behavior adapted from{' '} Swiper @@ -108,10 +108,10 @@ export function Footer() { © {year} Made by{' '} Lota diff --git a/site/components/InstallTabs.tsx b/site/components/InstallTabs.tsx index 0f60d54..55ca9d0 100644 --- a/site/components/InstallTabs.tsx +++ b/site/components/InstallTabs.tsx @@ -35,7 +35,7 @@ export function InstallTabs() { {TABS.map((key) => (