From 0d284b5c7d93dafcc6791a6cb61fb86676ea4b6a Mon Sep 17 00:00:00 2001 From: jwinterm Date: Fri, 16 Jan 2026 21:02:05 -0500 Subject: [PATCH] Upgrade Next.js to 15, fix first-load theme bug, add SVG assets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Upgrade next 14.1.0 → 15.5.9, next-themes 0.2.1 → 0.4.6 - Fix theme not initializing on first load after server restart - Add SVG logos and favicon, update site-logo to use SVGs - Create shared Header component with skip-to-main link - Add portfolio links to bento grid cards - Improve accessibility: aria-describedby, prefers-reduced-motion - Fix useFormState → useActionState for React 19 compatibility - Fix founder-signature icon (use square favicon instead of wide logo) --- apps/web/package.json | 6 +- apps/web/public/images/favicon.svg | 119 +++ apps/web/public/images/logo_dark.svg | 55 ++ apps/web/public/images/logo_dark_w_name.svg | 119 +++ apps/web/public/images/logo_light.svg | 55 ++ apps/web/public/images/logo_light_w_name.svg | 119 +++ apps/web/public/manifest.json | 5 + apps/web/src/app/about/page.tsx | 37 +- apps/web/src/app/globals.css | 16 +- apps/web/src/app/layout.tsx | 3 +- apps/web/src/app/page.tsx | 21 +- apps/web/src/app/services/page.tsx | 23 +- .../components/about/founder-signature.tsx | 29 +- .../src/components/contact/contact-form.tsx | 87 ++- .../components/landing/dynamic-background.tsx | 22 +- apps/web/src/components/layout/header.tsx | 41 ++ apps/web/src/components/layout/site-logo.tsx | 6 +- .../src/components/portfolio/bento-grid.tsx | 139 ++-- apps/web/src/components/theme-provider.tsx | 2 +- apps/web/src/components/theme-toggle.tsx | 31 +- packages/ui/src/procedural/data-grid.tsx | 2 +- packages/ui/src/procedural/nebula-field.tsx | 21 +- pnpm-lock.yaml | 676 +++++++++++++++--- 23 files changed, 1331 insertions(+), 303 deletions(-) create mode 100644 apps/web/public/images/favicon.svg create mode 100644 apps/web/public/images/logo_dark.svg create mode 100644 apps/web/public/images/logo_dark_w_name.svg create mode 100644 apps/web/public/images/logo_light.svg create mode 100644 apps/web/public/images/logo_light_w_name.svg create mode 100644 apps/web/src/components/layout/header.tsx diff --git a/apps/web/package.json b/apps/web/package.json index 6d0c9ee..6dc0632 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -14,8 +14,8 @@ "@repo/ui": "workspace:*", "framer-motion": "^11.0.0", "lucide-react": "^0.350.0", - "next": "14.1.0", - "next-themes": "^0.2.1", + "next": "^15.1.3", + "next-themes": "^0.4.6", "react": "^18.2.0", "react-dom": "^18.2.0", "react-icons": "^5.5.0", @@ -27,7 +27,7 @@ "@types/react-dom": "^18.0.0", "autoprefixer": "^10.0.1", "eslint": "^8", - "eslint-config-next": "14.1.0", + "eslint-config-next": "^15.1.3", "postcss": "^8", "tailwindcss": "^3.3.0", "tailwindcss-animate": "^1.0.7", diff --git a/apps/web/public/images/favicon.svg b/apps/web/public/images/favicon.svg new file mode 100644 index 0000000..9a7d282 --- /dev/null +++ b/apps/web/public/images/favicon.svg @@ -0,0 +1,119 @@ + + + + diff --git a/apps/web/public/images/logo_dark.svg b/apps/web/public/images/logo_dark.svg new file mode 100644 index 0000000..b1da386 --- /dev/null +++ b/apps/web/public/images/logo_dark.svg @@ -0,0 +1,55 @@ + + + + diff --git a/apps/web/public/images/logo_dark_w_name.svg b/apps/web/public/images/logo_dark_w_name.svg new file mode 100644 index 0000000..fee8962 --- /dev/null +++ b/apps/web/public/images/logo_dark_w_name.svg @@ -0,0 +1,119 @@ + + + +SuchSoftware diff --git a/apps/web/public/images/logo_light.svg b/apps/web/public/images/logo_light.svg new file mode 100644 index 0000000..774245f --- /dev/null +++ b/apps/web/public/images/logo_light.svg @@ -0,0 +1,55 @@ + + + + diff --git a/apps/web/public/images/logo_light_w_name.svg b/apps/web/public/images/logo_light_w_name.svg new file mode 100644 index 0000000..220d011 --- /dev/null +++ b/apps/web/public/images/logo_light_w_name.svg @@ -0,0 +1,119 @@ + + + +SuchSoftware diff --git a/apps/web/public/manifest.json b/apps/web/public/manifest.json index 533ead2..61faa2a 100644 --- a/apps/web/public/manifest.json +++ b/apps/web/public/manifest.json @@ -7,6 +7,11 @@ "background_color": "#000000", "theme_color": "#000000", "icons": [ + { + "src": "/images/favicon.svg", + "type": "image/svg+xml", + "purpose": "any" + }, { "src": "/images/favicon_192.png", "sizes": "192x192", diff --git a/apps/web/src/app/about/page.tsx b/apps/web/src/app/about/page.tsx index dadd7a0..34771b7 100644 --- a/apps/web/src/app/about/page.tsx +++ b/apps/web/src/app/about/page.tsx @@ -1,9 +1,7 @@ -import { DynamicBackground } from "@/components/landing/dynamic-background"; -import { ThemeToggle } from "@/components/theme-toggle"; import { FounderSignature } from "@/components/about/founder-signature"; -import { SiteLogo } from "@/components/layout/site-logo"; -import Link from "next/link"; +import { Header } from "@/components/layout/header"; import { FaMapMarkerAlt, FaPhone, FaBuilding, FaExternalLinkAlt } from "react-icons/fa"; +import { FaXTwitter, FaTelegram, FaReddit } from "react-icons/fa6"; import type { Metadata } from "next"; export const metadata: Metadata = { @@ -19,19 +17,9 @@ export const metadata: Metadata = { export default function AboutPage() { return (
- -
-
- - -
- -
+
-
+

About Us

@@ -78,8 +66,21 @@ export default function AboutPage() {

Connect

-

Find us on your favorite platforms

- {/* Social links are in the footer, but we can highlight them here or leave for footer */} +

Find us on your favorite platforms

+
diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css index 8bcd3b7..da800a2 100644 --- a/apps/web/src/app/globals.css +++ b/apps/web/src/app/globals.css @@ -87,7 +87,7 @@ overflow: hidden; transition: all 300ms; } - + .section-container { @apply mask-backdrop rounded-[2.5rem] p-12 md:p-20 text-card-foreground; } @@ -95,8 +95,20 @@ .glass-card { @apply mask-backdrop rounded-[2rem] p-10 text-card-foreground; } - + .glass-card:hover { border-color: hsl(var(--primary) / 0.3); } } + +/* Respect users who prefer reduced motion */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 292cf4b..cef57d0 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -12,9 +12,10 @@ export const metadata: Metadata = { description: "Next-generation web applications engineered for scale, compliance, and performance.", icons: { icon: [ + { url: "/images/favicon.svg", type: "image/svg+xml" }, { url: "/images/favicon_192.png", sizes: "192x192", type: "image/png" }, ], - shortcut: "/images/favicon_192.png", + shortcut: "/images/favicon.svg", apple: [ { url: "/images/favicon_192.png", sizes: "192x192", type: "image/png" }, ], diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index fa1f847..5068f86 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -1,30 +1,17 @@ -import { DynamicBackground } from "@/components/landing/dynamic-background"; -import { ThemeToggle } from "@/components/theme-toggle"; import { PortfolioGrid } from "@/components/portfolio/bento-grid"; import { ContactForm } from "@/components/contact/contact-form"; import { FounderSignature } from "@/components/about/founder-signature"; import { ServicesPreview } from "@/components/landing/services-preview"; -import { SiteLogo } from "@/components/layout/site-logo"; +import { Header } from "@/components/layout/header"; import Link from "next/link"; import { Button } from "@repo/ui/components/button"; -import { Card } from "@repo/ui/components/card"; export default function Home() { return (
- -
-
- - -
- -
- -
+
+ +

Digital Craftsmanship

diff --git a/apps/web/src/app/services/page.tsx b/apps/web/src/app/services/page.tsx index 26eaa07..c35dfea 100644 --- a/apps/web/src/app/services/page.tsx +++ b/apps/web/src/app/services/page.tsx @@ -1,7 +1,6 @@ import { DataGrid } from "@repo/ui/procedural/data-grid"; -import { ThemeToggle } from "@/components/theme-toggle"; import { Button } from "@repo/ui/components/button"; -import { SiteLogo } from "@/components/layout/site-logo"; +import { Header } from "@/components/layout/header"; import Link from "next/link"; import { Card } from "@repo/ui/components/card"; import type { Metadata } from "next"; @@ -19,22 +18,10 @@ export const metadata: Metadata = { export default function ServicesPage() { return (
- - {/* Option B: Data Grid Background for Technical Feel */} +
-
-
- - -
- -
- -
+
{/* Context Header */}
@@ -70,8 +57,8 @@ export default function ServicesPage() {

Zero Capital Upfront

- Our "Toast" style partnership means we win when you win. Low friction onboarding - with performance-based incentives. + Our revenue-share partnership means no setup fees or monthly subscriptions—we + only earn when you're making sales.

diff --git a/apps/web/src/components/about/founder-signature.tsx b/apps/web/src/components/about/founder-signature.tsx index 55764a1..e7f685d 100644 --- a/apps/web/src/components/about/founder-signature.tsx +++ b/apps/web/src/components/about/founder-signature.tsx @@ -7,12 +7,13 @@ export const FounderSignature = () => { {/* Left Column: Bio */}
-
- Such Software Icon + Such Software Icon
@@ -23,13 +24,13 @@ export const FounderSignature = () => {

- "During my three years at Cecil College, I didn't just teach web engineering—I re-engineered how it was taught. + "During my three years at Cecil College, I didn't just teach engineering—I re-engineered how it was taught.

- Transitioning the curriculum toward from PDFs and Powerpoints to web-based content, I focused on transforming complex technical lessons into highly accessible HTML using Quarto markdown as our source. This allowed us to consistently generate semantic content including accessible equations and guaranteed descriptive alt text for every visual asset. + Transitioning the curriculum from PDFs and Powerpoints to web-based content, I focused on transforming complex technical lessons into highly accessible HTML using Quarto markdown. This allowed us to consistently generate semantic content including accessible equations and guaranteed descriptive alt text for every visual asset.

- This wasn't just academic theory; it was a rigorous discipline of semantic architecture, ARIA compliance, and cognitive load management. Today, I bring that same technical rigor to Such Software’s commercial projects. We don't retroactively fix accessibility; we architect for it from line one." + This wasn't just theory; it was a methodical implementation of semantic architecture, ARIA compliance, and cognitive load management. Today, I bring that same technical rigor to Such Software’s commercial projects. We don't retroactively fix accessibility; we architect for it from line one."

@@ -61,18 +62,18 @@ export const FounderSignature = () => { 03 Core Competencies -
    +
    • - ✓ WCAG 2.2 AA Compliance + ✓ WCAG 2.2 AA Compliance
    • - ✓ Section 508 & ADA + ✓ Section 508 & ADA
    • - ✓ Semantic HTML5 Architecture + ✓ Semantic HTML5 Architecture
    • - ✓ Playwright A11y Automation + ✓ Playwright A11y Automation
    diff --git a/apps/web/src/components/contact/contact-form.tsx b/apps/web/src/components/contact/contact-form.tsx index 4799434..46a3173 100644 --- a/apps/web/src/components/contact/contact-form.tsx +++ b/apps/web/src/components/contact/contact-form.tsx @@ -1,16 +1,17 @@ 'use client'; -import { useFormState, useFormStatus } from 'react-dom'; +import { useActionState } from 'react'; +import { useFormStatus } from 'react-dom'; import { submitContactForm } from '@/actions/submit-contact'; import { TurnstileWidget } from './turnstile-widget'; -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef } from 'react'; import { Button } from '@repo/ui/components/button'; function SubmitButton() { const { pending } = useFormStatus(); return ( -