Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,36 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<!--
Applies the saved/system theme before first paint, avoiding a flash of the wrong
theme. Kept as a tiny inline script since it must run before any CSS is applied.
-->
<script>
(() => {
let isDark = false;
try {
const stored = localStorage.getItem("theme");
isDark =
stored === "dark" ||
(stored !== "light" && window.matchMedia("(prefers-color-scheme: dark)").matches);
} catch (_error) {
// Storage can be unavailable (private browsing); default to light.
}
document.documentElement.classList.toggle("dark", isDark);
})();
</script>

<!-- Favicon -->
<link rel="icon" href="/favicon.ico" />
<link rel="manifest" href="/manifest.json" />

<!-- Third-party origins this page actually loads from -->
<link rel="preconnect" href="https://static.wikia.nocookie.net" crossorigin />
<link rel="preconnect" href="https://cloud.umami.is" />

<!-- Theme color -->
<meta name="theme-color" content="#000000" />
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#ffffff" />
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#09090b" />

<!-- SEO Meta -->
<title>Maintainable Frontend Architecture with React</title>
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@
"@tailwindcss/vite": "^4.3.3",
"@tanstack/react-form": "^1.33.5",
"@tanstack/react-router": "^1.170.32",
"@tanstack/react-router-devtools": "^1.167.1",
"@tanstack/router-plugin": "^1.168.35",
"@tanstack/store": "^0.11.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
Expand All @@ -69,6 +67,8 @@
"@commitlint/cli": "21.2.2",
"@commitlint/config-conventional": "21.2.2",
"@playwright/test": "1.62.1",
"@tanstack/react-router-devtools": "^1.167.1",
"@tanstack/router-plugin": "^1.168.35",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^7.0.1",
"@testing-library/react": "^16.3.3",
Expand Down
12 changes: 6 additions & 6 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions public/manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"short_name": "TanStack App",
"name": "Create TanStack App Sample",
"short_name": "Azeroth's Finest Wares",
"name": "Azeroth's Finest Wares — Object Calisthenics Demo",
"description": "Interactive React demo showing how to apply Object Calisthenics rules for maintainable, well-structured front-end code.",
"icons": [
{
"src": "favicon.ico",
Expand All @@ -20,6 +21,6 @@
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"theme_color": "#ffffff",
"background_color": "#ffffff"
}
1 change: 0 additions & 1 deletion public/vite.svg

This file was deleted.

14 changes: 10 additions & 4 deletions src/app/main.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createRouter, RouterProvider } from "@tanstack/react-router";
import { StrictMode } from "react";
import ReactDOM from "react-dom/client";
import { ANALYTICS_CONFIG } from "@/config/analytics.config";
import { SITE_CONFIG } from "@/config/site.config";
// Import the generated route tree
import { routeTree } from "./routeTree.gen.ts";
Expand Down Expand Up @@ -45,7 +46,12 @@ if (rootElement && !rootElement.innerHTML) {
);
}

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
// Forward Core Web Vitals to Umami. window.umami is undefined until the analytics
// script has loaded, so this is a safe no-op if it never does.
reportWebVitals((metric) => {
window.umami?.track(ANALYTICS_CONFIG.events.webVital, {
name: metric.name,
value: Math.round(metric.name === "CLS" ? metric.value * 1000 : metric.value),
rating: metric.rating,
});
});
37 changes: 33 additions & 4 deletions src/app/routes/__root.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,50 @@
import { createRootRoute, Outlet } from "@tanstack/react-router";
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
import { MotionConfig } from "motion/react";
import { lazy, Suspense } from "react";
import { SITE_CONFIG } from "@/config/site.config";
import Header from "@/ui/components/Header";

const TanStackRouterDevtools = import.meta.env.DEV
? lazy(() =>
import("@tanstack/react-router-devtools").then((module) => ({
default: module.TanStackRouterDevtools,
})),
)
: () => null;

export const Route = createRootRoute({
component: () => (
<MotionConfig reducedMotion="user">
<div className="flex flex-col min-h-screen">
<div className="flex min-h-screen flex-col bg-background text-foreground">
<a
href="#main-content"
className="sr-only focus:not-sr-only focus:fixed focus:left-4 focus:top-4 focus:z-[100] focus:rounded-md focus:bg-primary focus:px-4 focus:py-2 focus:text-primary-foreground focus:shadow-lg"
>
Skip to main content
</a>
<Header />
<Outlet />
<TanStackRouterDevtools />
<div className="flex flex-1 flex-col">
<Outlet />
</div>
<footer className="border-t bg-muted/30 px-4 py-6 text-center text-sm text-muted-foreground sm:px-10">
<p>
A demo by{" "}
<a
href={SITE_CONFIG.author.githubUrl}
target="_blank"
rel="noopener noreferrer"
className="font-medium underline underline-offset-4 hover:text-foreground"
>
{SITE_CONFIG.author.name}
</a>
, illustrating Object Calisthenics in a React front end.
</p>
</footer>
{import.meta.env.DEV && (
<Suspense fallback={null}>
<TanStackRouterDevtools />
</Suspense>
)}
</div>
</MotionConfig>
),
Expand Down
24 changes: 12 additions & 12 deletions src/app/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ export const Route = createFileRoute("/")({
component: App,
});

const linkClassName =
"inline-flex items-center gap-2 font-semibold text-[#61dafb] transition-colors duration-200 hover:text-white hover:underline underline-offset-4 rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-[#61dafb] focus-visible:ring-offset-2 focus-visible:ring-offset-[#282c34]";
const secondaryLinkClassName =
"inline-flex items-center gap-2 rounded-sm font-semibold text-foreground underline-offset-4 outline-none transition-colors duration-200 hover:text-primary hover:underline focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background";

function App() {
return (
<main
id="main-content"
className="flex min-h-[calc(100vh-3.5rem)] flex-col items-center justify-center gap-8 bg-[#282c34] px-6 py-16 text-base text-white sm:text-lg md:text-xl lg:text-[1.375rem]"
className="flex flex-1 flex-col items-center justify-center gap-8 bg-gradient-to-b from-muted/40 to-background px-6 py-16 text-base text-foreground sm:text-lg md:text-xl lg:text-[1.375rem]"
>
<img
src={logo}
Expand All @@ -26,16 +26,16 @@ function App() {
/>

<div className="flex flex-col items-center gap-6">
<h1 className="max-w-3xl text-center text-4xl font-extrabold leading-tight">
<h1 className="max-w-3xl text-center text-3xl font-extrabold leading-tight sm:text-4xl">
Maintainable Frontend Architecture with React
</h1>
<p className="max-w-3xl text-center leading-relaxed text-gray-300">
<p className="max-w-3xl text-center leading-relaxed text-muted-foreground">
This site is a hands-on demo showing how to apply{" "}
<strong className="text-white">Object Calisthenics</strong> in a front-end app.
You'll see how keeping objects small, simple, and focused makes your React code
easier to understand and maintain.
<strong className="text-foreground">Object Calisthenics</strong> in a front-end
app. You'll see how keeping objects small, simple, and focused makes your React
code easier to understand and maintain.
</p>
<p className="max-w-3xl text-center leading-relaxed text-gray-300">
<p className="max-w-3xl text-center leading-relaxed text-muted-foreground">
Take a few moments to explore the interactive experience, then dive into the
code to see these principles in action.
</p>
Expand All @@ -44,7 +44,7 @@ function App() {
<div className="flex flex-col items-center gap-3 sm:flex-row sm:flex-wrap sm:justify-center">
<Link
to="/shopping-cart"
className="inline-flex items-center gap-2 rounded-md bg-[#61dafb] px-5 py-3 font-semibold text-[#282c34] transition-colors duration-200 hover:bg-white outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-[#282c34]"
className="inline-flex items-center gap-2 rounded-md bg-primary px-5 py-3 font-semibold text-primary-foreground shadow-xs transition-colors duration-200 hover:bg-primary/90 outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
data-umami-event={ANALYTICS_CONFIG.events.homeShoppingCart}
>
<ShoppingCart className="h-5 w-5" aria-hidden="true" />
Expand All @@ -56,7 +56,7 @@ function App() {
href={SITE_CONFIG.articleUrl}
target="_blank"
rel="noreferrer"
className={linkClassName}
className={secondaryLinkClassName}
data-umami-event={ANALYTICS_CONFIG.events.homeArticle}
>
<BookOpen className="h-4 w-4 shrink-0" aria-hidden="true" />
Expand All @@ -67,7 +67,7 @@ function App() {
href={SITE_CONFIG.repositoryUrl}
target="_blank"
rel="noopener noreferrer"
className={linkClassName}
className={secondaryLinkClassName}
data-umami-event={ANALYTICS_CONFIG.events.homeGithub}
>
<GitHubLogoIcon className="h-4 w-4 shrink-0" aria-hidden="true" />
Expand Down
2 changes: 1 addition & 1 deletion src/app/routes/shopping-cart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function ShoppingCartRoute() {
return (
<main
id="main-content"
className="relative min-h-screen bg-gray-50 py-8 px-4 sm:px-6 lg:px-8"
className="relative flex-1 bg-muted/30 py-8 px-4 sm:px-6 lg:px-8"
>
<ShoppingCart welcomeModalHandle={ref} />
<WelcomeModal
Expand Down
1 change: 0 additions & 1 deletion src/assets/react.svg

This file was deleted.

1 change: 1 addition & 0 deletions src/config/analytics.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ export const ANALYTICS_CONFIG = {
addToCart: "product.add-to-cart",
watchVideo: "product.watch-video",
readLore: "product.read-lore",
webVital: "web-vitals.report",
},
} as const;
22 changes: 12 additions & 10 deletions src/reportWebVitals.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
const reportWebVitals = (onPerfEntry?: () => void) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import("web-vitals").then(({ onCLS, onINP, onFCP, onLCP, onTTFB }) => {
onCLS(onPerfEntry);
onINP(onPerfEntry);
onFCP(onPerfEntry);
onLCP(onPerfEntry);
onTTFB(onPerfEntry);
});
}
import type { Metric } from "web-vitals";

const reportWebVitals = (onReport?: (metric: Metric) => void) => {
if (!onReport) return;

import("web-vitals").then(({ onCLS, onINP, onFCP, onLCP, onTTFB }) => {
onCLS(onReport);
onINP(onReport);
onFCP(onReport);
onLCP(onReport);
onTTFB(onReport);
});
};

export default reportWebVitals;
68 changes: 0 additions & 68 deletions src/styles/index.css

This file was deleted.

Loading
Loading