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:
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex flex-col items-center gap-2 p-4 rounded-lg border border-border-default bg-panel hover:border-accent-border transition-colors duration-fast cursor-pointer group"
|
||||
role="button"
|
||||
:tabindex="0"
|
||||
:aria-label="type"
|
||||
@click="$emit('select', type)"
|
||||
@keydown.enter="$emit('select', type)"
|
||||
>
|
||||
<svg
|
||||
:width="size"
|
||||
:height="size"
|
||||
viewBox="0 0 48 48"
|
||||
fill="none"
|
||||
class="text-secondary group-hover:text-accent transition-colors duration-fast"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<!-- Line Array -->
|
||||
<template v-if="type === 'line-array'">
|
||||
<rect x="6" y="16" width="8" height="16" rx="1" fill="currentColor" />
|
||||
<path d="M16 20 L24 14 M16 28 L24 34" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M26 12 L30 10 M26 36 L30 38" stroke="currentColor" stroke-width="2" stroke-linecap="round" opacity="0.6" />
|
||||
<path d="M32 8 L36 6 M32 40 L36 42" stroke="currentColor" stroke-width="2" stroke-linecap="round" opacity="0.3" />
|
||||
</template>
|
||||
|
||||
<!-- Subwoofer -->
|
||||
<template v-else-if="type === 'subwoofer'">
|
||||
<rect x="8" y="8" width="32" height="32" rx="2" fill="none" stroke="currentColor" stroke-width="2" />
|
||||
<circle cx="24" cy="24" r="8" fill="none" stroke="currentColor" stroke-width="2" />
|
||||
<circle cx="24" cy="24" r="4" fill="none" stroke="currentColor" stroke-width="1.5" />
|
||||
</template>
|
||||
|
||||
<!-- Point Source -->
|
||||
<template v-else-if="type === 'point-source'">
|
||||
<rect x="10" y="18" width="10" height="12" rx="1" fill="currentColor" />
|
||||
<path d="M22 16 Q28 24 22 32" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M26 12 Q34 24 26 36" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" opacity="0.6" />
|
||||
</template>
|
||||
|
||||
<!-- Column Array -->
|
||||
<template v-else-if="type === 'column-array'">
|
||||
<rect x="16" y="6" width="16" height="36" rx="1" fill="none" stroke="currentColor" stroke-width="2" />
|
||||
<circle cx="24" cy="14" r="3" fill="currentColor" />
|
||||
<circle cx="24" cy="24" r="3" fill="currentColor" />
|
||||
<circle cx="24" cy="34" r="3" fill="currentColor" />
|
||||
</template>
|
||||
|
||||
<!-- Monitor -->
|
||||
<template v-else-if="type === 'monitor'">
|
||||
<path d="M6 30 L24 14 L42 30 L42 34 L6 34 Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round" />
|
||||
<circle cx="24" cy="26" r="4" fill="none" stroke="currentColor" stroke-width="1.5" />
|
||||
</template>
|
||||
|
||||
<!-- Moving Head -->
|
||||
<template v-else>
|
||||
<rect x="18" y="6" width="12" height="10" rx="1" fill="currentColor" />
|
||||
<path d="M24 16 L24 24" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
|
||||
<circle cx="24" cy="30" r="6" fill="none" stroke="currentColor" stroke-width="2" />
|
||||
<path d="M18 30 L14 30 M30 30 L34 30" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.5" />
|
||||
</template>
|
||||
</svg>
|
||||
<span class="text-xs text-text-muted group-hover:text-accent transition-colors duration-fast">{{ label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = withDefaults(defineProps<{
|
||||
type: "line-array" | "subwoofer" | "point-source" | "column-array" | "monitor" | "moving-head";
|
||||
label?: string;
|
||||
size?: number;
|
||||
}>(), {
|
||||
label: "",
|
||||
size: 48,
|
||||
});
|
||||
|
||||
const labelMap: Record<string, string> = {
|
||||
"line-array": "Line Array",
|
||||
"subwoofer": "Subwoofer",
|
||||
"point-source": "Point Source",
|
||||
"column-array": "Column Array",
|
||||
"monitor": "Monitor",
|
||||
"moving-head": "Moving Head",
|
||||
};
|
||||
|
||||
const label = computed(() => props.label || labelMap[props.type] || props.type);
|
||||
|
||||
defineEmits<{
|
||||
select: [type: string];
|
||||
}>();
|
||||
</script>
|
||||
Reference in New Issue
Block a user