T01: Frontend Foundation – Nuxt 3 + Tailwind + Design Tokens + Layout + Meta

- Nuxt 3 project with @nuxtjs/tailwindcss module
- Tailwind CSS config with A0 Dark Theme design tokens (CSS custom properties)
- AppHeader: sticky, logo, nav, phone, social icons, mobile burger menu
- AppFooter: 4-column grid (Navigation, Kontakt, Rechtliches, Social)
- HmsLogo: SVG with orange #EC6925 border
- error.vue: 404 page with Seite nicht gefunden
- robots.txt server route: Disallow: /
- Meta: noindex, nofollow, noarchive, nosnippet
- JSON-LD: LocalBusiness schema
- OpenGraph tags
- routeRules: SSR/CSR per route
- Shared components: LoadingSkeleton, EmptyState, ErrorState, LegalContentPage, SpeakerIcon
- 42 vitest tests passing
- Build: 0 errors
This commit is contained in:
A0 Implementation Engineer
2026-07-09 01:17:35 +02:00
parent 1c3e5fd932
commit 3bfa54b4b3
34 changed files with 14152 additions and 0 deletions
+99
View File
@@ -0,0 +1,99 @@
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
describe("Design Tokens", () => {
const cssPath = resolve(__dirname, "../../assets/css/main.css");
const cssContent = readFileSync(cssPath, "utf-8");
it("should define --bg variable in :root", () => {
expect(cssContent).toContain("--bg: #131313");
});
it("should define --panel variable in :root", () => {
expect(cssContent).toContain("--panel: #1a1a1a");
});
it("should define --color-accent variable in :root", () => {
expect(cssContent).toContain("--color-accent: #EC6925");
});
it("should define --surface variable in :root", () => {
expect(cssContent).toContain("--surface: #212121");
});
it("should define --card variable in :root", () => {
expect(cssContent).toContain("--card: #2d2d2d");
});
it("should define --border variable in :root", () => {
expect(cssContent).toContain("--border: #444444a8");
});
it("should define --text variable in :root", () => {
expect(cssContent).toContain("--text: #ffffff");
});
it("should define --text-muted variable in :root", () => {
expect(cssContent).toContain("--text-muted: #d4d4d4");
});
it("should define radius tokens", () => {
expect(cssContent).toContain("--radius-sm: 2px");
expect(cssContent).toContain("--radius-md: 3px");
expect(cssContent).toContain("--radius-lg: 4px");
});
it("should define transition tokens", () => {
expect(cssContent).toContain("--transition-fast: 150ms ease");
});
});
describe("Tailwind Config", () => {
const configPath = resolve(__dirname, "../../tailwind.config.ts");
const configContent = readFileSync(configPath, "utf-8");
it("should extend colors with accent", () => {
expect(configContent).toContain("accent");
expect(configContent).toContain("#EC6925");
});
it("should extend colors with bg", () => {
expect(configContent).toContain('bg: "#131313"');
});
it("should extend colors with panel", () => {
expect(configContent).toContain('panel: "#1a1a1a"');
});
it("should configure Inter font family", () => {
expect(configContent).toContain("Inter");
});
});
describe("Nuxt Config", () => {
const configPath = resolve(__dirname, "../../nuxt.config.ts");
const configContent = readFileSync(configPath, "utf-8");
it("should include noindex robots meta tag", () => {
expect(configContent).toContain("noindex");
expect(configContent).toContain("nofollow");
expect(configContent).toContain("noarchive");
expect(configContent).toContain("nosnippet");
});
it("should include JSON-LD LocalBusiness script", () => {
expect(configContent).toContain("LocalBusiness");
expect(configContent).toContain("schema.org");
});
it("should include routeRules for SSR/CSR", () => {
expect(configContent).toContain("routeRules");
expect(configContent).toContain("/mietkatalog");
expect(configContent).toContain("/admin");
});
it("should include Tailwind CSS module", () => {
expect(configContent).toContain("@nuxtjs/tailwindcss");
});
});
+136
View File
@@ -0,0 +1,136 @@
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
describe("AppHeader Component", () => {
const componentPath = resolve(__dirname, "../../components/AppHeader.vue");
const content = readFileSync(componentPath, "utf-8");
it("should contain sticky positioning", () => {
expect(content).toContain("sticky");
expect(content).toContain("top-0");
});
it("should have nav items: Home, Referenzen, Mietkatalog, Kontakt", () => {
expect(content).toContain("Home");
expect(content).toContain("Referenzen");
expect(content).toContain("Mietkatalog");
expect(content).toContain("Kontakt");
});
it("should include phone link tel:+491726264796", () => {
expect(content).toContain("tel:+491726264796");
});
it("should include Facebook social icon link", () => {
expect(content).toContain("facebook.com");
});
it("should include Instagram social icon link", () => {
expect(content).toContain("instagram.com");
});
it("should have mobile burger menu with aria-expanded", () => {
expect(content).toContain("aria-expanded");
expect(content).toContain("mobileMenuOpen");
});
it("should have min touch target height for nav items", () => {
expect(content).toContain("min-h-44px");
});
});
describe("AppFooter Component", () => {
const componentPath = resolve(__dirname, "../../components/AppFooter.vue");
const content = readFileSync(componentPath, "utf-8");
it("should contain address with Leipheim", () => {
expect(content).toContain("89340");
expect(content).toContain("Leipheim");
});
it("should contain phone number +49 8221 204433", () => {
expect(content).toContain("tel:+498221204433");
expect(content).toContain("204433");
});
it("should contain email link info@hms-licht-ton.de", () => {
expect(content).toContain("mailto:info@hms-licht-ton.de");
});
it("should contain Impressum link", () => {
expect(content).toContain("Impressum");
expect(content).toContain("/impressum");
});
it("should contain DSGVO link", () => {
expect(content).toContain("DSGVO");
expect(content).toContain("/datenschutz");
});
it("should contain AGB Vermietung link", () => {
expect(content).toContain("AGB Vermietung");
expect(content).toContain("/agb-vermietung");
});
it("should NOT contain 'Neu in der Vermietung'", () => {
expect(content).not.toContain("Neu in der Vermietung");
});
it("should NOT contain 'AGB Shop'", () => {
expect(content).not.toContain("AGB Shop");
});
it("should have 4-column grid layout", () => {
expect(content).toContain("lg:grid-cols-4");
});
});
describe("Error Page (404)", () => {
const errorPath = resolve(__dirname, "../../error.vue");
const content = readFileSync(errorPath, "utf-8");
it("should contain 'Seite nicht gefunden' for 404", () => {
expect(content).toContain("Seite nicht gefunden");
});
it("should contain link to home /", () => {
expect(content).toContain('to="/"');
});
it("should include noindex meta tag", () => {
expect(content).toContain("noindex");
expect(content).toContain("nofollow");
});
});
describe("HmsLogo Component", () => {
const logoPath = resolve(__dirname, "../../components/HmsLogo.vue");
const content = readFileSync(logoPath, "utf-8");
it("should render SVG element", () => {
expect(content).toContain("<svg");
});
it("should use orange accent color #EC6925", () => {
expect(content).toContain("#EC6925");
});
it("should contain border/stroke with accent color", () => {
expect(content).toContain("accentColor");
expect(content).toContain("stroke");
});
});
describe("robots.txt route", () => {
const robotsPath = resolve(__dirname, "../../server/routes/robots.txt.ts");
const content = readFileSync(robotsPath, "utf-8");
it("should contain User-agent: *", () => {
expect(content).toContain("User-agent: *");
});
it("should contain Disallow: /", () => {
expect(content).toContain("Disallow: /");
});
});