From 426b91b29a31718ef77c8c31b726b2f19a1c3f4b Mon Sep 17 00:00:00 2001 From: Teakowa Date: Sun, 27 Sep 2026 17:58:46 +0800 Subject: [PATCH 01/10] feat: align site copy with goal.md and refine UI Co-authored-by: Indent --- src/app.css | 169 ++++--- src/lib/components/AgentsSection.svelte | 45 +- src/lib/components/CapabilitiesSection.svelte | 35 -- .../components/CompatibilitySection.svelte | 97 ---- src/lib/components/CopyButton.svelte | 107 ++-- src/lib/components/EcosystemSection.svelte | 92 ++-- src/lib/components/Footer.svelte | 29 +- src/lib/components/Header.svelte | 157 +++--- src/lib/components/Hero.svelte | 52 +- src/lib/components/InstallSection.svelte | 203 ++++---- src/lib/components/LanguagesSection.svelte | 52 ++ src/lib/components/OpenSourceSection.svelte | 35 -- src/lib/components/PillarsSection.svelte | 106 ---- src/lib/components/Section.svelte | 17 +- src/lib/components/Terminal.svelte | 38 +- src/lib/components/ToolingSection.svelte | 18 + src/lib/site.ts | 459 +++++++++--------- src/routes/+layout.svelte | 2 +- src/routes/+page.svelte | 20 +- static/install.ps1 | 279 +++++++++++ 20 files changed, 1084 insertions(+), 928 deletions(-) delete mode 100644 src/lib/components/CapabilitiesSection.svelte delete mode 100644 src/lib/components/CompatibilitySection.svelte create mode 100644 src/lib/components/LanguagesSection.svelte delete mode 100644 src/lib/components/OpenSourceSection.svelte delete mode 100644 src/lib/components/PillarsSection.svelte create mode 100644 src/lib/components/ToolingSection.svelte create mode 100644 static/install.ps1 diff --git a/src/app.css b/src/app.css index 298652f..d876eca 100644 --- a/src/app.css +++ b/src/app.css @@ -2,9 +2,10 @@ /* * Visual system. - * Neutral ink scale + one orange accent. Type is the platform system stack - * (optical sizing and tracking tables already in the OS fonts) — not Inter. - * Mono is reserved for actual code, not chrome. + * Neutral ink scale + one orange accent, dark appearance only. + * Type is the platform system stack (optical sizing and tracking tables ship + * with the OS fonts). Mono is reserved for actual code, not chrome. + * Tracking is size-specific: display sizes tighten, small labels loosen. */ @theme { --color-ink-50: #f7f7f5; @@ -23,17 +24,27 @@ --color-accent-500: #f97316; --color-accent-600: #e05e0a; - --font-sans: - system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + --font-sans: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; --font-mono: ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas, monospace; - --text-hero: clamp(2rem, 5.5vw + 0.5rem, 4.25rem); - --text-hero--line-height: 1.05; - --text-hero--letter-spacing: -0.028em; + --text-hero: clamp(2.25rem, 4.4vw + 0.75rem, 4rem); + --text-hero--line-height: 1.04; + --text-hero--letter-spacing: -0.032em; - --text-title: clamp(1.375rem, 2vw + 0.75rem, 2rem); - --text-title--line-height: 1.2; - --text-title--letter-spacing: -0.018em; + --text-title: clamp(1.625rem, 2.2vw + 0.9rem, 2.5rem); + --text-title--line-height: 1.1; + --text-title--letter-spacing: -0.024em; + + --text-lead: clamp(1.0625rem, 0.4vw + 0.95rem, 1.25rem); + --text-lead--line-height: 1.5; + --text-lead--letter-spacing: -0.008em; + + /* Radii: surfaces read as larger; controls nest inside with a smaller radius. */ + --radius-control: 0.625rem; + --radius-card: 1rem; + + /* Settles without overshoot, like a critically damped spring. */ + --ease-settle: cubic-bezier(0.25, 1, 0.5, 1); } @layer base { @@ -47,7 +58,7 @@ body { background-color: var(--color-ink-950); - color: var(--color-ink-200); + color: var(--color-ink-300); font-family: var(--font-sans); font-size: 1rem; line-height: 1.55; @@ -57,7 +68,6 @@ overflow-x: clip; } - /* Prevent pre elements from causing page-level overflow */ pre { max-width: 100%; overflow-x: auto; @@ -72,7 +82,7 @@ :focus-visible { outline: 2px solid var(--color-accent-500); outline-offset: 3px; - border-radius: 2px; + border-radius: 4px; } } @@ -81,16 +91,9 @@ margin-inline: auto; min-width: 0; width: 100%; - max-width: 68rem; - padding-left: max(1rem, env(safe-area-inset-left)); - padding-right: max(1rem, env(safe-area-inset-right)); - } - - @media (min-width: 480px) { - .container-site { - padding-left: max(1.25rem, env(safe-area-inset-left)); - padding-right: max(1.25rem, env(safe-area-inset-right)); - } + max-width: 70rem; + padding-left: max(1.25rem, env(safe-area-inset-left)); + padding-right: max(1.25rem, env(safe-area-inset-right)); } @media (min-width: 640px) { @@ -100,36 +103,66 @@ } } + /* Floating translucent chrome; content scrolls underneath. */ .chrome { position: relative; isolation: isolate; - background: rgb(16 16 14 / 0.82); + background: rgb(16 16 14 / 0.72); backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%); } + /* Scroll edge effect instead of a hard divider. */ + .chrome::after { + content: ''; + position: absolute; + inset-inline: 0; + top: 100%; + height: 1.25rem; + pointer-events: none; + background: linear-gradient(to bottom, rgb(16 16 14 / 0.55), transparent); + } + + .surface { + border-radius: var(--radius-card); + background-color: rgb(26 26 23 / 0.6); + box-shadow: + inset 0 0 0 1px rgb(247 247 245 / 0.06), + inset 0 1px 0 rgb(247 247 245 / 0.04); + } + + .eyebrow { + font-size: 0.8125rem; + line-height: 1.3; + letter-spacing: 0.01em; + font-weight: 600; + color: var(--color-accent-400); + } + .btn { display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem; min-height: 2.75rem; - padding: 0.6rem 1.1rem; - border-radius: 2px; + padding: 0.625rem 1.25rem; + border-radius: 999px; font-size: 0.9375rem; font-weight: 590; line-height: 1.2; letter-spacing: -0.01em; touch-action: manipulation; transition: - background-color 140ms ease, - border-color 140ms ease, - color 140ms ease, - transform 120ms cubic-bezier(0.2, 0, 0, 1); + background-color 160ms ease, + box-shadow 160ms ease, + color 160ms ease, + transform 160ms var(--ease-settle); } + /* Feedback lives on the press and is immediate. */ .btn:active { - transform: scale(0.96); + transform: scale(0.97); + transition-duration: 80ms; } .btn-primary { @@ -137,37 +170,41 @@ color: var(--color-ink-950); } - .btn-primary:hover { - background-color: var(--color-accent-400); - } - .btn-ghost { - border: 1px solid var(--color-ink-700); color: var(--color-ink-100); + box-shadow: inset 0 0 0 1px var(--color-ink-700); } - .btn-ghost:hover { - border-color: var(--color-ink-400); - background-color: var(--color-ink-900); + @media (hover: hover) { + .btn-primary:hover { + background-color: var(--color-accent-400); + } + + .btn-ghost:hover { + background-color: var(--color-ink-900); + box-shadow: inset 0 0 0 1px var(--color-ink-500); + } } - .path { - font-family: var(--font-mono); - font-size: 0.75rem; - letter-spacing: 0.01em; - color: var(--color-ink-500); + .text-link { + color: var(--color-ink-100); + text-decoration: underline; + text-decoration-color: var(--color-ink-600); + text-underline-offset: 4px; + transition: + color 160ms ease, + text-decoration-color 160ms ease; } - .scroll-target { - scroll-margin-top: calc(4rem + env(safe-area-inset-top)); + @media (hover: hover) { + .text-link:hover { + color: var(--color-ink-50); + text-decoration-color: var(--color-ink-300); + } } - .terminal pre { - overflow-x: auto; - white-space: pre; - -webkit-overflow-scrolling: touch; - scrollbar-width: thin; - scrollbar-color: var(--color-ink-700) transparent; + .scroll-target { + scroll-margin-top: calc(3.5rem + env(safe-area-inset-top)); } .no-scrollbar { @@ -186,23 +223,19 @@ } } +/* Reduced motion keeps opacity/color feedback but drops movement and scaling. */ @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } - *, - *::before, - *::after { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - scroll-behavior: auto !important; - } - .btn:active { transform: none; } + + [data-motion='move'] { + transition-property: opacity, background-color, color !important; + } } @media (prefers-reduced-transparency: reduce) { @@ -222,15 +255,25 @@ .chrome { background: var(--color-ink-950); backdrop-filter: none; + -webkit-backdrop-filter: none; border-bottom: 1px solid var(--color-ink-50); } + .chrome::after { + display: none; + } + + .surface { + background-color: var(--color-ink-900); + box-shadow: inset 0 0 0 1px var(--color-ink-300); + } + .btn-primary { outline: 1px solid var(--color-ink-50); outline-offset: 2px; } .btn-ghost { - border-color: var(--color-ink-200); + box-shadow: inset 0 0 0 1px var(--color-ink-200); } } diff --git a/src/lib/components/AgentsSection.svelte b/src/lib/components/AgentsSection.svelte index 3b0b36f..25d30c6 100644 --- a/src/lib/components/AgentsSection.svelte +++ b/src/lib/components/AgentsSection.svelte @@ -1,21 +1,38 @@ -
-
-
-

{agents.title}

-

{agents.lead}

-
+
+
+ {#each agents.columns as column (column.title)} +
+

{column.title}

+
    + {#each column.points as point (point)} +
  • + + {point} +
  • + {/each} +
+
+ {/each} +
-
- {#each agents.points as point (point.title)} -
-

{point.title}

-

{point.body}

-
+
+

{agents.loop.title}

+

{agents.loop.lead}

+
    + {#each agents.loop.steps as step, i (step.title)} +
  1. + {String(i + 1).padStart(2, '0')} +

    {step.title}

    +

    {step.body}

    +
  2. {/each} -
+
-
+
diff --git a/src/lib/components/CapabilitiesSection.svelte b/src/lib/components/CapabilitiesSection.svelte deleted file mode 100644 index 283b747..0000000 --- a/src/lib/components/CapabilitiesSection.svelte +++ /dev/null @@ -1,35 +0,0 @@ - - -
-
-
-

{capabilities.title}

-

- {capabilities.lead} -

-
- -
- {#each capabilities.items as item (item.id)} -
-
-

{item.title}

-

- {item.body} -

-
- -
-
- {item.command} - -
-
-
- {/each} -
-
-
diff --git a/src/lib/components/CompatibilitySection.svelte b/src/lib/components/CompatibilitySection.svelte deleted file mode 100644 index b04e250..0000000 --- a/src/lib/components/CompatibilitySection.svelte +++ /dev/null @@ -1,97 +0,0 @@ - - -
-
-
-

{compatibility.title}

-

- {compatibility.lead} -

-
- - -
- {#each compatibility.surfaces as surface (surface.name)} -
-
-

{surface.name}

- - {surface.status} - -
-

{surface.role}

-

- {surface.details} -

-

- Owned by: {surface.owner} -

-
- {/each} -
- - -
-
-

- Conversion directions -

-
-
    - {#each compatibility.conversionDirections as dir (dir.from + dir.to)} -
  • -
    - {dir.from} - → - {dir.to} -
    - - {dir.status} - -
  • - {/each} -
-
- - -
-
-

- {compatibility.sdne.title} -

-

- {compatibility.sdne.lead} -

-
- Priority: {compatibility.sdne.priority} -
-
- -
- {#each compatibility.sdne.levels as level (level.letter)} -
-
- {level.letter} - ({level.name}) -
-

- {level.desc} -

-
- {/each} -
-
-
-
diff --git a/src/lib/components/CopyButton.svelte b/src/lib/components/CopyButton.svelte index 14f4ca0..886ebee 100644 --- a/src/lib/components/CopyButton.svelte +++ b/src/lib/components/CopyButton.svelte @@ -2,99 +2,68 @@ let { text, label = 'Copy', - copiedLabel = 'Copied', - variant = 'ghost' + showLabel = false }: { text: string; + /** Accessible name; shown next to the icon when showLabel is true. */ label?: string; - copiedLabel?: string; - variant?: 'ghost' | 'primary' | 'inline'; + showLabel?: boolean; } = $props(); let copied = $state(false); let timeoutId: ReturnType | undefined; + function writeFallback(value: string) { + const textarea = document.createElement('textarea'); + textarea.value = value; + textarea.style.position = 'fixed'; + textarea.style.opacity = '0'; + document.body.appendChild(textarea); + textarea.select(); + const ok = document.execCommand('copy'); + document.body.removeChild(textarea); + if (!ok) throw new Error('copy failed'); + } + async function handleCopy() { try { - await navigator.clipboard.writeText(text); - copied = true; - if (typeof navigator !== 'undefined' && 'vibrate' in navigator) { - try { - navigator.vibrate(12); - } catch {} + if (navigator.clipboard?.writeText) { + await navigator.clipboard.writeText(text); + } else { + writeFallback(text); } - if (timeoutId) clearTimeout(timeoutId); - timeoutId = setTimeout(() => { - copied = false; - }, 2000); } catch { - // Fallback: execCommand for older environments - try { - const textarea = document.createElement('textarea'); - textarea.value = text; - textarea.style.position = 'fixed'; - textarea.style.opacity = '0'; - document.body.appendChild(textarea); - textarea.select(); - document.execCommand('copy'); - document.body.removeChild(textarea); - copied = true; - if (typeof navigator !== 'undefined' && 'vibrate' in navigator) { - try { - navigator.vibrate(12); - } catch {} - } - if (timeoutId) clearTimeout(timeoutId); - timeoutId = setTimeout(() => { - copied = false; - }, 2000); - } catch { - // Silently fail if clipboard denied - } + return; } + copied = true; + clearTimeout(timeoutId); + timeoutId = setTimeout(() => (copied = false), 1600); } + + $effect(() => () => clearTimeout(timeoutId)); diff --git a/src/lib/components/EcosystemSection.svelte b/src/lib/components/EcosystemSection.svelte index 0fc656a..8e41fa1 100644 --- a/src/lib/components/EcosystemSection.svelte +++ b/src/lib/components/EcosystemSection.svelte @@ -1,48 +1,56 @@ -
-
-
-

{ecosystem.title}

-

- {ecosystem.lead} -

-
+
+
    + {#each ecosystem.principles as principle (principle.title)} +
  • +

    {principle.title}

    +

    {principle.body}

    +
  • + {/each} +
-
-
    - {#each ecosystem.items as item (item.repo)} -
  • -
    -
    - - {item.repo} - - {item.role} -
    - - {item.status} - -
    -

    - {item.desc} -

    -
  • - {/each} -
-
+ + +
+

{ecosystem.nonGoals.title}

+
    + {#each ecosystem.nonGoals.items as item (item)} +
  • {item}
  • + {/each} +
-
+
diff --git a/src/lib/components/Footer.svelte b/src/lib/components/Footer.svelte index 5afd123..db1617e 100644 --- a/src/lib/components/Footer.svelte +++ b/src/lib/components/Footer.svelte @@ -3,25 +3,18 @@ import { site, footer } from '$lib/site'; -