Skip to content

appsome/website

Repository files navigation

Handoff: Appsome! marketing site — SvelteKit + Tailwind v4 + shadcn-svelte

Overview

A prerendered marketing website for Appsome! (software & hardware development studio), built on the Appsome! Blueprint design system. This package is a SvelteKit scaffold wired to the design tokens, ready for your team (or Claude Code) to flesh out and ship.

It replaces the earlier React preview that lived in ui_kits/marketing-site/ (now removed). The visual language, copy and layout are identical — only the framework changed.

About these files

The .svelte/.ts/.css files here are real, working source — not throwaway HTML mocks. The site is built out: the token bridge, configs, chrome (Nav / Footer), the committed centered hero, the one-pager home (Services · Work preview · Process · Contact), the All Work list (/work), two project detail templates (Case Study + App Landing) behind one [slug] route, a "trusted by" customer strip, and the standalone contact route are all implemented. Recreate everything in your environment's conventions; review before shipping.

src/lib/components/ui/ is the design system's component library, copied in 1:1. All 12 primitives (Button, IconButton, Tag, Badge, Avatar, StatBlock, Input, Toggle, Card, BlueprintFrame, Callout, Tabs) are the exact same .svelte files that live in the design-system project — a single source of truth. They're self-contained Svelte 5 components styled with scoped <style> blocks that read the design tokens (CSS custom properties); they pull in no Tailwind utilities and no npm deps, so they drop into any Svelte project and stay in sync if you re-copy them. Keep them in sync by re-copying from the design system rather than editing here.

Fidelity

High-fidelity. Exact colors, type, spacing and interactions come from the design-system tokens. Build pixel-faithfully; don't re-interpret the look.


Stack & decisions (per your choices)

Concern Choice
Framework SvelteKit (Svelte 5 runes: $props, $state, $derived)
Styling Tailwind v4 (CSS-first, @theme in src/app.css — no tailwind.config.js)
Tokens → Tailwind Mapped into the theme, backed by the system's CSS vars (bg-accent, text-brand, border-navy-700, font-display, rounded-md all work)
Components Design-system Svelte 5 primitives (ui/, copied 1:1 from the DS — scoped styles + CSS vars, no Tailwind/npm deps). shadcn-svelte (bits-ui + tailwind-variants) optional for any extra primitives you need
Adapter adapter-static — whole site prerendered to HTML
Icons @lucide/svelte (matches the system's Lucide choice)

Project structure

appsome-marketing-site/
├─ package.json                 # deps (SvelteKit, Tailwind v4, bits-ui, lucide)
├─ svelte.config.js             # adapter-static + $lib alias  (code in §Config files)
├─ vite.config.ts               # @tailwindcss/vite + sveltekit  (code in §Config files)
├─ tsconfig.json
├─ static/
│  ├─ appsome-logo-white.png    # brand wordmark (copied from the DS)
│  └─ favicon.png
└─ src/
   ├─ app.html                  # html shell (class="dark", navy bg)
   ├─ app.css                   # ★ Tailwind v4 entry + @theme token bridge
   ├─ lib/
   │  ├─ utils.ts               # cn() — clsx + tailwind-merge
   │  ├─ styles/
   │  │  ├─ tokens.css          # @imports the token files below
   │  │  └─ tokens/             # colors / typography / spacing / effects / fonts / base
   │  │     └─ …                # ← copied verbatim from the design system
   │  └─ components/
   │     ├─ Nav.svelte          # sticky blur nav + mobile menu (one-pager anchors + /work)
   │     ├─ Footer.svelte
   │     ├─ Hero.svelte         # 3 variants; home uses CENTERED (committed)
   │     ├─ SectionHead.svelte  # eyebrow + title + sub
   │     ├─ CustomerLogos.svelte# ★ "trusted by" strip (placeholder wordmarks)
   │     ├─ sections/           # ★ one-pager home sections
   │     │  ├─ Services.svelte      ├─ WorkPreview.svelte (featured + "See all work →")
   │     │  ├─ Process.svelte       └─ ContactBlock.svelte (slim: email + message)
   │     ├─ work/               # ★ work surfaces
   │     │  ├─ WorkTile.svelte       # card tile (home preview)
   │     │  ├─ WorkRow.svelte        # full-width list row (/work)
   │     │  ├─ CaseStudyDetail.svelte
   │     │  ├─ AppLandingDetail.svelte
   │     │  ├─ StoreBadge.svelte     # App Store / Google Play / Mac App Store / Setapp
   │     │  ├─ PhoneFrame.svelte     # CSS phone bezel + slot
   │     │  ├─ DesktopFrame.svelte   # CSS window chrome + slot
   │     │  └─ ScreenshotSlot.svelte # drop-in image slot / placeholder
   │     └─ ui/                 # ★ design-system primitives, copied 1:1 (single source)
   │        ├─ Button.svelte        ├─ IconButton.svelte
   │        ├─ Tag.svelte           ├─ Badge.svelte
   │        ├─ Avatar.svelte        ├─ StatBlock.svelte
   │        ├─ Input.svelte         ├─ Toggle.svelte
   │        ├─ Card.svelte          ├─ BlueprintFrame.svelte
   │        ├─ Callout.svelte       └─ Tabs.svelte
   ├─ lib/data/projects.ts      # ★ project content + types (case-study | app)
   └─ routes/
      ├─ +layout.svelte         # imports app.css, wraps Nav/main/Footer
      ├─ +layout.ts             # prerender = true
      ├─ +page.svelte           # ★ Home one-pager (centered hero + sections)
      ├─ work/+page.svelte      # ★ All Work — full-width row list
      ├─ projects/[slug]/
      │  ├─ +page.ts            # entries() → prerendered slugs
      │  └─ +page.svelte        # ★ router: branches on project.type
      └─ contact/+page.svelte   # standalone full contact form (NDA toggle)

The token bridge (the important part)

src/lib/styles/tokens/ is copied verbatim from the design system — it's the single source of truth (:root { --navy-900, --accent, --brand, --space-*, … }, the @font-face/webfont @import, and the .bp-grid / .bp-grid-major / .bp-dots / .bp-ticks helper classes).

src/app.css then:

  1. @import 'tailwindcss';
  2. @import '$lib/styles/tokens.css'; (the raw vars + helpers)
  3. Re-exposes a curated subset as Tailwind theme tokens referencing the vars:
    @theme {
      --color-accent: var(--accent);
      --color-brand: var(--brand);
      --color-navy-900: var(--navy-900);
      --font-display: 'Space Grotesk', sans-serif;
      --radius-md: 8px;
      /* …see app.css for the full map */
    }
    → utilities like bg-accent, text-ink-secondary, border-navy-700, font-mono, rounded-lg are generated, and because they point at the CSS vars, editing a token in the design system flows straight through.

Text colors are exposed as ink / ink-secondary / ink-muted / ink-faint / annotation (so text-ink-muted etc.). shadcn-svelte's expected vars (--background, --primary, --ring, …) are aliased to brand tokens in an @layer base block so generated components inherit the look.

⚠️ Fonts load from the Google Fonts CDN via tokens/fonts.css. To self-host, drop woff2 files in static/fonts/ and swap the @import for @font-face rules.


Setup

npm create svelte@latest appsome-marketing-site   # Skeleton, TypeScript
cd appsome-marketing-site
npm i -D tailwindcss @tailwindcss/vite @sveltejs/adapter-static
npm i @lucide/svelte bits-ui tailwind-variants clsx tailwind-merge
npx shadcn-svelte@latest init        # choose CSS vars; point at src/app.css
# then drop in the files from this handoff (configs, app.css, lib/, routes/, static/)
npm run dev

Add shadcn components only if you need primitives beyond the 12 in ui/: npx shadcn-svelte@latest add <name>, then restyle with the mapped utilities (bg-brand, border-border-strong, …). The ui/ primitives are the design system's own components (scoped styles + CSS vars) — prefer them, and keep them in sync by re-copying from the design system rather than editing in place.

Config files

These two build configs import npm packages, so they're listed here rather than as live files in the bundle (the design-system project they sit in would try to bundle them). Create them at the project root verbatim:

svelte.config.js

import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

/** @type {import('@sveltejs/kit').Config} */
const config = {
  preprocess: vitePreprocess(),
  kit: {
    adapter: adapter({ pages: 'build', assets: 'build', fallback: undefined, strict: true }),
    alias: { $lib: 'src/lib' }
  }
};
export default config;

vite.config.ts

import { sveltekit } from '@sveltejs/kit/vite';
import tailwindcss from '@tailwindcss/vite';
import { defineConfig } from 'vite';

// Tailwind v4 runs as a Vite plugin — no tailwind.config.js. Theme lives in src/app.css.
export default defineConfig({
  plugins: [tailwindcss(), sveltekit()]
});

src/lib/utils.ts ships a dependency-free cn() so the scaffold compiles cleanly; swap it for the clsx + tailwind-merge version (both already in package.json) in production — the swap is documented inline in that file.


Screens / views

1 · Home (one-pager) — src/routes/+page.svelte

Per your direction, Services / Work / Process / Contact are collapsed onto the home page, each with an anchor id (#services, #work, #process, #contact) the nav links scroll to (scroll-mt-24 offsets the sticky nav). Order: Hero → Customer strip → Services → Work preview → Process → Contact.

  • Herocommitted to CENTERED (Hero variant="centered"). Centered statement “We draft it. / We dimension it. / We build it to spec.” over a full-bleed .bp-grid-major with a radial vignette; CTAs scroll to #contact / #services; 3 StatBlocks. (Hero.svelte still contains the split & panel variants for reference — delete them once you're sure.)
  • Customer stripCustomerLogos.svelte, a quiet “trusted by” band right under the hero. No customer logo files were supplied, so it renders neutral wordmark placeholders (NORTHWIND, VECTORA, …) at low opacity. Replace each with a real logo: drop PNG/SVGs in static/customers/ and swap the <span> for <img src="/customers/acme.svg" …>. No links, per spec.
  • Servicessections/Services.svelte. 4-up grid of Card (eyebrow 01 · ENGINEERING …, icon, body, mono footer); interactive gives the cyan hairline + lift.
  • Work previewsections/WorkPreview.svelte. Section head + “See all work →” (→ /work); the featured projects (featuredProjects from the data file) as WorkTiles. App-type projects get a small “App” marker.
  • Processsections/Process.svelte, id="process", .bp-grid band. 4 steps with a mono STEP 0N kicker + connecting arrow on desktop.
  • Contactsections/ContactBlock.svelte, id="contact". **Slimmed to email
    • message** per spec: copy + mailto: on the left, a two-field form (email, message) on the right with a Callout success state and a “Need an NDA or more detail? →” link to the full /contact route.

2 · All Work — src/routes/work/+page.svelte

The “all work” list you asked for. Page hero + a full-width row list (WorkRow): each row = index + sector·year, title (with an “App” marker for app projects) + summary + tags, and a status Badge + arrow on the right. Hover lifts the row tint + accents the title/arrow. Rows link to /projects/[slug]. Driven by the full projects array.

3 · Project detail — src/routes/projects/[slug]/+page.svelte

A thin router: looks up the project by slug and branches on project.type, rendering one of two templates. Unknown slugs show a small 404 block; prerendered slugs are listed in +page.ts entries() (keep in sync with the data file).

  • Case Studywork/CaseStudyDetail.svelte. Back link, sector eyebrow, title, intro, tags → 4-up StatBlock strip → two-column brief/“what we built” + a BlueprintFrame spec aside → “Start a build like this” CTA.
  • App Landingwork/AppLandingDetail.svelte, for mobile/desktop apps. Hero (copy + tagline + store badges + platforms meta, with 1–2 PhoneFrames fanned beside it) → Screens gallery of PhoneFrames → optional Desktop section (DesktopFrame, shown only if desktopShot is set) → 3 highlight cards → CTA repeating the store badges.
    • Store badges (StoreBadge.svelte) support App Store, Google Play, Mac App Store, Setapp — rendered as Appsome-styled CTA buttons (icon + kicker + name), not reproductions of the official badge artwork; restyle/replace with official badges at your discretion.
    • Screenshots are drop-in slots (ScreenshotSlot.svelte): each Shot in the data shows a labelled placeholder with the recommended export size until you set its src to a /static path. Phone frames are CSS bezels (1290×2796 ratio); desktop frame is a CSS window at 16:10 — swap for real device art if you prefer.

4 · Contact (standalone) — src/routes/contact/+page.svelte

The fuller enquiry form (name, email, company, message, NDA toggle), linked from the home contact block’s “Need an NDA or more detail?”. Bordered form card using Input / Toggle / Button with a Callout success state, plus a details aside. Replace the fake submit with a SvelteKit form action or API call.

Project content — src/lib/data/projects.ts

One typed projects array feeds the home preview, /work, and the detail router. Each project declares type: 'case-study' | 'app', featured, status, tags, and template-specific fields (stats/brief/spec for case studies; tagline/stores/phoneShots/desktopShot/highlights for apps). Move this to a CMS/markdown + a load function for production; the shape stays the same.


Interactions & behavior

  • Nav: sticky, bg-navy-950/70 + backdrop-blur-md, hairline bottom border; links highlight text-accent on the active route; collapses to a burger menu below md (toggled with $state).
  • Buttons: hover brightens one step (brand → brand-hover, accent → accent-bright); press active:scale-[0.98]; 150ms color transitions.
  • Cards/tiles: hover → cyan hairline (border-accent) + -translate-y-[3px]
    • shadow-lg, 200ms.
  • Inputs: focus → border-accent + cyan glow shadow.
  • Hero switcher: dev aid only — remove before launch.
  • Motion: fades + ≤8px translate; easings --ease-out/--ease-snap from the tokens; honor prefers-reduced-motion; no infinite loops.
  • Responsive: nav burger ≤ md; grids reflow (4→2→1) at md/sm.

State management

Local component state only (Svelte 5 $state) — nav open, hero variant, contact sent/nda. No global store needed. Project content should move to a load function (CMS/markdown) rather than the inline placeholder map.

Design tokens

Full set in src/lib/styles/tokens/. Key values:

  • Ground --bg #060c18, surface --surface #0a1322, raised #0d1b30, panel #102338.
  • Brand #0276bd (hover #1f8ed4). Accent cyan #36c5f0 (bright #5fd4f5).
  • Text #eef4fb / #7ba2c8 / #4d7fac; annotation #8fe2f9.
  • Status success #43d98a, warning #f5a623, danger #f7615f.
  • Type Space Grotesk (display) · IBM Plex Sans (body) · IBM Plex Mono (labels, UPPERCASE, tracking 0.14em).
  • Radii 3/5/8/12/18px. Shadows ink-tinted rgba(2,8,20,…).
  • Spacing 4px module; container 1200px; blueprint grid unit 24px / major 120px.

Assets

  • static/appsome-logo-white.png — wordmark (white, transparent). Navy/cyan variants + favicons live in the design system's assets/.
  • static/favicon.png — the A! avatar.
  • Icons: @lucide/svelte.

Files in this bundle

Everything under handoff/sveltekit-marketing-site/. Start at src/app.css (token bridge), then Hero.svelte, then the routes.

Voice reminder

Engineering-confident: precise, understated, “we”/“you”, sentence case for prose, UPPERCASE mono for labels/specs, no emoji. Lean on real numbers/specs only.

Releases

No releases published

Packages

 
 
 

Contributors