Init
This commit is contained in:
132
docs/design-examples/components/ExperimentaButton.vue
Normal file
132
docs/design-examples/components/ExperimentaButton.vue
Normal file
@@ -0,0 +1,132 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* ExperimentaButton Component
|
||||
*
|
||||
* Experimenta-branded button with animated gradient background.
|
||||
* Based on the experimenta Design System.
|
||||
*
|
||||
* @example
|
||||
* <ExperimentaButton>Click me</ExperimentaButton>
|
||||
* <ExperimentaButton variant="secondary">Cancel</ExperimentaButton>
|
||||
* <ExperimentaButton size="small">Small</ExperimentaButton>
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
/** Button variant */
|
||||
variant?: 'primary' | 'secondary'
|
||||
/** Button size */
|
||||
size?: 'small' | 'medium' | 'large'
|
||||
/** Disabled state */
|
||||
disabled?: boolean
|
||||
/** Button type */
|
||||
type?: 'button' | 'submit' | 'reset'
|
||||
/** Link behavior (renders as <a> tag) */
|
||||
href?: string
|
||||
/** Target for links */
|
||||
target?: '_blank' | '_self' | '_parent' | '_top'
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
variant: 'primary',
|
||||
size: 'large',
|
||||
disabled: false,
|
||||
type: 'button',
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
click: [event: MouseEvent]
|
||||
}>()
|
||||
|
||||
function handleClick(event: MouseEvent) {
|
||||
emit('click', event)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component
|
||||
:is="href ? 'a' : 'button'"
|
||||
:href="href"
|
||||
:target="href ? target : undefined"
|
||||
:type="!href ? type : undefined"
|
||||
:disabled="!href ? disabled : undefined"
|
||||
:class="[
|
||||
'btn-experimenta',
|
||||
`btn-${variant}`,
|
||||
`btn-${size}`,
|
||||
{
|
||||
'btn-disabled': disabled,
|
||||
},
|
||||
]"
|
||||
@click="handleClick"
|
||||
>
|
||||
<slot />
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Base Button Styles */
|
||||
.btn-experimenta {
|
||||
@apply inline-block cursor-pointer;
|
||||
@apply font-medium text-white;
|
||||
@apply transition-all;
|
||||
@apply outline-0 border-0;
|
||||
@apply rounded-2xl;
|
||||
text-decoration: none;
|
||||
line-height: 1.7em;
|
||||
}
|
||||
|
||||
/* Primary Variant */
|
||||
.btn-primary {
|
||||
background: #e6007e;
|
||||
background-image: linear-gradient(to left, #e6007e, #e6007e, #e40521, #e6007e);
|
||||
background-size: 300%;
|
||||
background-position: left;
|
||||
transition:
|
||||
background-position 1s ease,
|
||||
all 0.3s ease;
|
||||
}
|
||||
|
||||
.btn-primary:hover:not(.btn-disabled) {
|
||||
background-position: right;
|
||||
}
|
||||
|
||||
/* Secondary Variant */
|
||||
.btn-secondary {
|
||||
@apply bg-transparent border-2 border-accent text-accent;
|
||||
}
|
||||
|
||||
.btn-secondary:hover:not(.btn-disabled) {
|
||||
@apply bg-accent text-white;
|
||||
}
|
||||
|
||||
/* Sizes */
|
||||
.btn-large {
|
||||
@apply text-lg px-8 py-2.5;
|
||||
}
|
||||
|
||||
.btn-medium {
|
||||
@apply text-base px-6 py-2;
|
||||
}
|
||||
|
||||
.btn-small {
|
||||
@apply text-base px-6 py-2;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 480px) {
|
||||
.btn-large {
|
||||
@apply text-base px-6 py-2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Disabled State */
|
||||
.btn-disabled {
|
||||
@apply opacity-50 cursor-not-allowed;
|
||||
}
|
||||
|
||||
/* Focus State (Accessibility) */
|
||||
.btn-experimenta:focus-visible {
|
||||
@apply outline-none ring-2 ring-accent ring-offset-2;
|
||||
ring-offset-color: var(--color-purple-darkest, #0f051d);
|
||||
}
|
||||
</style>
|
||||
115
docs/design-examples/components/ExperimentaCard.vue
Normal file
115
docs/design-examples/components/ExperimentaCard.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* ExperimentaCard Component
|
||||
*
|
||||
* Glass-morphism card component based on the experimenta Design System.
|
||||
*
|
||||
* @example
|
||||
* <ExperimentaCard>Content here</ExperimentaCard>
|
||||
* <ExperimentaCard variant="info">Info section</ExperimentaCard>
|
||||
* <ExperimentaCard title="Card Title">Content</ExperimentaCard>
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
/** Card variant */
|
||||
variant?: 'glass' | 'info' | 'contact' | 'progress'
|
||||
/** Card title (optional) */
|
||||
title?: string
|
||||
/** Title color (only for info/contact variants) */
|
||||
titleColor?: 'accent' | 'primary'
|
||||
/** Show left accent border */
|
||||
accentBorder?: boolean
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
variant: 'glass',
|
||||
titleColor: 'accent',
|
||||
accentBorder: false,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="[
|
||||
'card-experimenta',
|
||||
`card-${variant}`,
|
||||
{
|
||||
'card-accent-border': accentBorder || variant !== 'glass',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<!-- Title Slot or Prop -->
|
||||
<h3 v-if="title || $slots.title" :class="['card-title', `title-${titleColor}`]">
|
||||
<slot name="title">{{ title }}</slot>
|
||||
</h3>
|
||||
|
||||
<!-- Main Content -->
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Base Card Styles */
|
||||
.card-experimenta {
|
||||
@apply rounded-xl;
|
||||
@apply p-8 md:p-6 sm:p-5;
|
||||
@apply transition-all duration-300;
|
||||
}
|
||||
|
||||
/* Glass-morphism Variant (Main Card) */
|
||||
.card-glass {
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05));
|
||||
backdrop-filter: blur(15px);
|
||||
@apply border border-white/20;
|
||||
@apply shadow-glass;
|
||||
@apply rounded-2xl;
|
||||
@apply p-15 md:p-10 sm:p-8;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.card-glass {
|
||||
@apply rounded-lg;
|
||||
}
|
||||
}
|
||||
|
||||
/* Info Variant */
|
||||
.card-info {
|
||||
@apply bg-white/8;
|
||||
}
|
||||
|
||||
/* Contact Variant */
|
||||
.card-contact {
|
||||
@apply bg-white/8;
|
||||
@apply text-center;
|
||||
}
|
||||
|
||||
/* Progress Variant */
|
||||
.card-progress {
|
||||
@apply bg-white/8;
|
||||
@apply rounded-2xl md:rounded-lg;
|
||||
}
|
||||
|
||||
/* Accent Border (Left) */
|
||||
.card-accent-border {
|
||||
@apply border-l-4 border-accent;
|
||||
}
|
||||
|
||||
/* Card Title */
|
||||
.card-title {
|
||||
@apply font-medium mb-4;
|
||||
@apply text-lg md:text-base;
|
||||
}
|
||||
|
||||
.title-accent {
|
||||
@apply text-accent;
|
||||
}
|
||||
|
||||
.title-primary {
|
||||
@apply text-primary;
|
||||
}
|
||||
|
||||
/* Hover Effect (Optional) */
|
||||
.card-experimenta:hover {
|
||||
@apply shadow-2xl;
|
||||
}
|
||||
</style>
|
||||
216
docs/design-examples/components/ExperimentaLogo.vue
Normal file
216
docs/design-examples/components/ExperimentaLogo.vue
Normal file
@@ -0,0 +1,216 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* ExperimentaLogo Component
|
||||
*
|
||||
* Official experimenta Science Center logo (X-Logo with gradients).
|
||||
* SVG is taken from the design templates.
|
||||
*
|
||||
* @example
|
||||
* <ExperimentaLogo />
|
||||
* <ExperimentaLogo size="small" />
|
||||
* <ExperimentaLogo href="https://www.experimenta.science" />
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
/** Logo size */
|
||||
size?: 'small' | 'medium' | 'large'
|
||||
/** Link URL (if logo should be clickable) */
|
||||
href?: string
|
||||
/** Link target */
|
||||
target?: '_blank' | '_self'
|
||||
/** Accessible label */
|
||||
ariaLabel?: string
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
size: 'large',
|
||||
href: undefined,
|
||||
target: '_self',
|
||||
ariaLabel: 'experimenta Science Center Logo',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component
|
||||
:is="href ? 'a' : 'div'"
|
||||
:href="href"
|
||||
:target="href ? target : undefined"
|
||||
:class="[
|
||||
'logo-wrapper',
|
||||
{
|
||||
'logo-clickable': href,
|
||||
},
|
||||
]"
|
||||
:aria-label="ariaLabel"
|
||||
>
|
||||
<svg
|
||||
:class="['logo-svg', `logo-${size}`]"
|
||||
viewBox="0 0 382.94 87.17"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
role="img"
|
||||
:aria-label="ariaLabel"
|
||||
>
|
||||
<defs>
|
||||
<!-- Gradients for logo -->
|
||||
<linearGradient
|
||||
id="logo-gradient-a"
|
||||
x1="102.63"
|
||||
y1="152.32"
|
||||
x2="135.19"
|
||||
y2="191.11"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0.16" stop-color="#bf144c" />
|
||||
<stop offset="0.29" stop-color="#ce0f60" />
|
||||
<stop offset="0.47" stop-color="#de0b75" />
|
||||
<stop offset="0.59" stop-color="#e4097d" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="logo-gradient-b"
|
||||
x1="104.87"
|
||||
y1="170.45"
|
||||
x2="104.87"
|
||||
y2="170.45"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0.16" stop-color="#bf144c" />
|
||||
<stop offset="0.29" stop-color="#ab1a4e" />
|
||||
<stop offset="0.43" stop-color="#9f1d4f" />
|
||||
<stop offset="0.57" stop-color="#9b1e4f" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="logo-gradient-c"
|
||||
x1="68.79"
|
||||
y1="182.84"
|
||||
x2="154.66"
|
||||
y2="182.84"
|
||||
xlink:href="#logo-gradient-b"
|
||||
/>
|
||||
<linearGradient
|
||||
id="logo-gradient-d"
|
||||
x1="94.04"
|
||||
y1="182.21"
|
||||
x2="114.5"
|
||||
y2="126"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0.22" stop-color="#e4097d" />
|
||||
<stop offset="0.32" stop-color="#e4115e" />
|
||||
<stop offset="0.45" stop-color="#e5193d" />
|
||||
<stop offset="0.55" stop-color="#e51e28" />
|
||||
<stop offset="0.62" stop-color="#e52021" />
|
||||
<stop offset="0.9" stop-color="#f7a822" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- X Logo -->
|
||||
<polygon
|
||||
points="143.78 50 151.18 39.6 144.43 39.6 139.68 46.33 135.13 39.6 127.79 39.6 135.32 50.08 127.29 61.23 134.09 61.23 139.3 53.78 144.43 61.23 151.59 61.23 143.78 50"
|
||||
fill="#fff"
|
||||
/>
|
||||
|
||||
<!-- "experimenta" Text -->
|
||||
<path
|
||||
d="M245.79,175.33a9.2,9.2,0,0,0-1.85-3.39,7.28,7.28,0,0,0-2.9-2,10.29,10.29,0,0,0-3.65-.63c-3.12,0-5.47.95-7,2.82l-.56-2.23h-7.72v5.29h3.13v24.7h6.13v-8.4a8.29,8.29,0,0,0,1.7.42,17.3,17.3,0,0,0,2.6.19,11.8,11.8,0,0,0,4.53-.82,9.29,9.29,0,0,0,3.39-2.39,10.78,10.78,0,0,0,2.11-3.78,15.69,15.69,0,0,0,.74-5A16,16,0,0,0,245.79,175.33Zm-12.76,0a5.26,5.26,0,0,1,2.73-.75,4,4,0,0,1,3.12,1.4,5.85,5.85,0,0,1,1.25,4,10.56,10.56,0,0,1-.4,3.12,5.84,5.84,0,0,1-1.1,2.09,4.11,4.11,0,0,1-1.62,1.18,7.15,7.15,0,0,1-4.21.12,4.71,4.71,0,0,1-1.43-.58v-8.48A3.79,3.79,0,0,1,233,175.35Z"
|
||||
transform="translate(-68.76 -130.29)"
|
||||
fill="#fff"
|
||||
/>
|
||||
<!-- Additional text paths omitted for brevity - see design-example1.html for full SVG -->
|
||||
|
||||
<!-- Gradient Logo Mark (X with colors) -->
|
||||
<path
|
||||
d="M93.1,181.53l20.42-19.22c.87-.76,1.61-1.66,2.61-1.8,1.19-.17,3,1.09,3.3,1.28s10.7,6.64,12.57,7.77l15.75,9.71c4.25-1.29,7.2-4.59,7.46-7.92a5.92,5.92,0,0,0-3-5.65l-2.48-1.53h0s-17.5-10.32-22.43-13.18a22.83,22.83,0,0,0-11-3c-4.71.09-8.62,2.32-12.24,5h0l-24,18.2,3.64,4.7a16.1,16.1,0,0,0,6.48,4.83A17.88,17.88,0,0,0,93.1,181.53Z"
|
||||
transform="translate(-68.76 -130.29)"
|
||||
fill="url(#logo-gradient-a)"
|
||||
/>
|
||||
<path
|
||||
d="M104.87,170.45h0"
|
||||
transform="translate(-68.76 -130.29)"
|
||||
fill="url(#logo-gradient-b)"
|
||||
/>
|
||||
<path
|
||||
d="M93.1,181.53h0l11.77-11.08a3.11,3.11,0,0,1-1.35-.18,4.24,4.24,0,0,1-1.33-1.11l-7.57-8.93-14.57,11-7.26,5.5s-4.11,2.67-4,6.32c.15,4.65,5.34,6.69,5.34,6.69l30.63,13.13a35.93,35.93,0,0,0,11.38,2.53c3.39.08,6.83-1.14,10.49-2.24l24.76-8.46c1.26-.41,3.26-1.65,3.26-3.17,0-1.2-1.57-2.73-3.33-3.51a16.28,16.28,0,0,0-8.57-1.19c-4.7.44-25.95,7.65-25.95,7.65L93.16,184.23a1.28,1.28,0,0,1-.91-1.58A2.68,2.68,0,0,1,93.1,181.53Z"
|
||||
transform="translate(-68.76 -130.29)"
|
||||
fill="url(#logo-gradient-c)"
|
||||
/>
|
||||
<path
|
||||
d="M147.75,179.27c4.25-1.29,7.2-4.59,7.46-7.92a5.92,5.92,0,0,0-3-5.65l-2.48-1.53L132,169.56l-27.13.89h0a3.11,3.11,0,0,1-1.35-.18,4.24,4.24,0,0,1-1.33-1.11l-7.57-8.93-14.57,11,3.64,4.7a16.1,16.1,0,0,0,6.48,4.83,17.88,17.88,0,0,0,2.93.73h0a66.73,66.73,0,0,0,7.06.19C107.65,182,144.08,180.38,147.75,179.27Z"
|
||||
transform="translate(-68.76 -130.29)"
|
||||
fill="#e4097d"
|
||||
/>
|
||||
<path
|
||||
d="M104.87,170.45a3.26,3.26,0,0,1-1.35-.18,4.24,4.24,0,0,1-1.33-1.11l-7.67-9-3-3.55a1.94,1.94,0,0,1-.41-1.27,2.18,2.18,0,0,1,1-1.83L110,141.9a6.43,6.43,0,0,1,1.81-.87,5.82,5.82,0,0,1,1.86.09l16.49,2.64a24.38,24.38,0,0,0,9.39-.52c1.94-.49,4.31-1.25,5.39-2.91s-.6-2.9-1.11-3.27c-2.08-1.5-4.67-1.92-7.18-2.32l-25.48-4.08a23.84,23.84,0,0,0-10.88.57A19.61,19.61,0,0,0,95,133.6L71.44,149.36a6.34,6.34,0,0,0-1.08,9.38l.21.27,13.12,17a16.1,16.1,0,0,0,6.48,4.83,17.88,17.88,0,0,0,2.93.73Z"
|
||||
transform="translate(-68.76 -130.29)"
|
||||
fill="url(#logo-gradient-d)"
|
||||
/>
|
||||
</svg>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Logo Wrapper */
|
||||
.logo-wrapper {
|
||||
@apply flex items-center;
|
||||
@apply transition-all duration-300;
|
||||
}
|
||||
|
||||
.logo-clickable {
|
||||
@apply cursor-pointer no-underline;
|
||||
}
|
||||
|
||||
.logo-clickable:hover .logo-svg {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
/* Logo SVG */
|
||||
.logo-svg {
|
||||
@apply h-auto;
|
||||
@apply transition-transform duration-300;
|
||||
filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.3));
|
||||
}
|
||||
|
||||
/* Logo Sizes */
|
||||
.logo-large {
|
||||
@apply w-[300px];
|
||||
}
|
||||
|
||||
.logo-medium {
|
||||
@apply w-[250px];
|
||||
}
|
||||
|
||||
.logo-small {
|
||||
@apply w-[200px];
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.logo-large {
|
||||
@apply w-[250px] max-w-[90%];
|
||||
}
|
||||
|
||||
.logo-medium {
|
||||
@apply w-[200px] max-w-[85%];
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.logo-large {
|
||||
@apply w-[200px] max-w-[85%];
|
||||
}
|
||||
|
||||
.logo-medium {
|
||||
@apply w-[180px] max-w-[80%];
|
||||
}
|
||||
|
||||
.logo-small {
|
||||
@apply w-[150px] max-w-[75%];
|
||||
}
|
||||
}
|
||||
|
||||
/* Focus State */
|
||||
.logo-clickable:focus-visible {
|
||||
@apply outline-none ring-2 ring-accent ring-offset-2;
|
||||
ring-offset-color: var(--color-purple-darkest, #0f051d);
|
||||
}
|
||||
</style>
|
||||
125
docs/design-examples/components/ExperimentaStatusMessage.vue
Normal file
125
docs/design-examples/components/ExperimentaStatusMessage.vue
Normal file
@@ -0,0 +1,125 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* ExperimentaStatusMessage Component
|
||||
*
|
||||
* Status message component with animated icon.
|
||||
* Used for success, error, warning, and info states.
|
||||
*
|
||||
* @example
|
||||
* <ExperimentaStatusMessage type="success" title="Erfolg!">
|
||||
* Ihre Aktion war erfolgreich.
|
||||
* </ExperimentaStatusMessage>
|
||||
*
|
||||
* <ExperimentaStatusMessage type="error" title="Fehler">
|
||||
* Ein Fehler ist aufgetreten.
|
||||
* </ExperimentaStatusMessage>
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
/** Status type */
|
||||
type: 'success' | 'error' | 'warning' | 'info'
|
||||
/** Status title */
|
||||
title?: string
|
||||
/** Custom icon (overrides default) */
|
||||
icon?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
icon: undefined,
|
||||
})
|
||||
|
||||
// Default icons for each type
|
||||
const defaultIcons = {
|
||||
success: '✓',
|
||||
error: '✖',
|
||||
warning: '!',
|
||||
info: 'i',
|
||||
}
|
||||
|
||||
const displayIcon = computed(() => props.icon || defaultIcons[props.type])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="status-message">
|
||||
<!-- Animated Status Icon -->
|
||||
<div :class="['status-icon', `status-icon-${type}`]" role="img" :aria-label="`${type} icon`">
|
||||
{{ displayIcon }}
|
||||
</div>
|
||||
|
||||
<!-- Title -->
|
||||
<h1 v-if="title || $slots.title" class="status-title">
|
||||
<slot name="title">{{ title }}</slot>
|
||||
</h1>
|
||||
|
||||
<!-- Message Content -->
|
||||
<div class="status-content">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Status Message Container */
|
||||
.status-message {
|
||||
@apply text-center;
|
||||
}
|
||||
|
||||
/* Status Icon */
|
||||
.status-icon {
|
||||
@apply flex items-center justify-center;
|
||||
@apply w-25 h-25 rounded-full;
|
||||
@apply text-6xl text-white;
|
||||
@apply mb-8 mx-auto;
|
||||
@apply animate-pulse;
|
||||
}
|
||||
|
||||
/* Icon Colors by Type */
|
||||
.status-icon-success {
|
||||
@apply bg-success;
|
||||
}
|
||||
|
||||
.status-icon-error {
|
||||
@apply bg-error;
|
||||
}
|
||||
|
||||
.status-icon-warning {
|
||||
@apply bg-warning;
|
||||
}
|
||||
|
||||
.status-icon-info {
|
||||
@apply bg-info;
|
||||
}
|
||||
|
||||
/* Responsive Icon Size */
|
||||
@media (max-width: 480px) {
|
||||
.status-icon {
|
||||
@apply text-5xl;
|
||||
}
|
||||
}
|
||||
|
||||
/* Status Title */
|
||||
.status-title {
|
||||
@apply text-4xl md:text-3xl sm:text-2xl;
|
||||
@apply font-light tracking-tight;
|
||||
@apply mb-8;
|
||||
@apply text-white;
|
||||
}
|
||||
|
||||
/* Status Content */
|
||||
.status-content {
|
||||
@apply text-lg md:text-base sm:text-sm;
|
||||
@apply text-white/90;
|
||||
@apply leading-relaxed;
|
||||
}
|
||||
|
||||
/* Pulse Animation Override (Custom) */
|
||||
@keyframes pulse {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
400
docs/design-examples/components/README.md
Normal file
400
docs/design-examples/components/README.md
Normal file
@@ -0,0 +1,400 @@
|
||||
# experimenta Vue Komponenten-Beispiele
|
||||
|
||||
Dieser Ordner enthält **Referenz-Implementierungen** der experimenta Design System Komponenten als Vue 3 Single File Components (SFC).
|
||||
|
||||
Diese Komponenten dienen als **Vorlagen und Beispiele** für die Entwicklung eigener Komponenten oder können direkt in das Projekt kopiert werden.
|
||||
|
||||
---
|
||||
|
||||
## Verfügbare Komponenten
|
||||
|
||||
### 1. ExperimentaButton.vue
|
||||
|
||||
Animierter Button mit Gradient-Hintergrund nach experimenta Design System.
|
||||
|
||||
**Features:**
|
||||
|
||||
- Primary & Secondary Variants
|
||||
- Responsive Größen (Small, Medium, Large)
|
||||
- Link-Verhalten (kann als `<a>` oder `<button>` gerendert werden)
|
||||
- Hover-Animation mit Gradient-Shift
|
||||
- Accessibility-Ready (Focus States, ARIA Labels)
|
||||
|
||||
**Verwendung:**
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import ExperimentaButton from './ExperimentaButton.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Primary Button (default) -->
|
||||
<ExperimentaButton @click="handleClick"> Zur Startseite </ExperimentaButton>
|
||||
|
||||
<!-- Secondary Button -->
|
||||
<ExperimentaButton variant="secondary"> Abbrechen </ExperimentaButton>
|
||||
|
||||
<!-- As Link -->
|
||||
<ExperimentaButton href="https://www.experimenta.science" target="_blank">
|
||||
Zur experimenta Website
|
||||
</ExperimentaButton>
|
||||
|
||||
<!-- Small Size -->
|
||||
<ExperimentaButton size="small"> Small Button </ExperimentaButton>
|
||||
|
||||
<!-- Disabled -->
|
||||
<ExperimentaButton disabled> Disabled Button </ExperimentaButton>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. ExperimentaCard.vue
|
||||
|
||||
Glass-morphism Card-Komponente mit verschiedenen Variants.
|
||||
|
||||
**Features:**
|
||||
|
||||
- Glass-morphism Styling (Backdrop Blur)
|
||||
- Info, Contact, Progress Variants
|
||||
- Optional: Akzent-Border (links)
|
||||
- Slot-basiertes Design (flexibel)
|
||||
|
||||
**Verwendung:**
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import ExperimentaCard from './ExperimentaCard.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Glass Card (Main) -->
|
||||
<ExperimentaCard>
|
||||
<h1>Willkommen!</h1>
|
||||
<p>Dies ist eine Glass-morphism Card.</p>
|
||||
</ExperimentaCard>
|
||||
|
||||
<!-- Info Card mit Titel -->
|
||||
<ExperimentaCard variant="info" title="Ihre Vorteile">
|
||||
<p>Mit der Jahreskarte erhalten Sie...</p>
|
||||
</ExperimentaCard>
|
||||
|
||||
<!-- Card mit Custom Title Slot -->
|
||||
<ExperimentaCard variant="contact">
|
||||
<template #title>
|
||||
<span>Kontakt</span>
|
||||
</template>
|
||||
<p>E-Mail: info@experimenta.science</p>
|
||||
</ExperimentaCard>
|
||||
|
||||
<!-- Progress Card -->
|
||||
<ExperimentaCard variant="progress">
|
||||
<!-- Progress Bar Content -->
|
||||
</ExperimentaCard>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. ExperimentaStatusMessage.vue
|
||||
|
||||
Status-Nachrichten mit animierten Icons (Success, Error, Warning, Info).
|
||||
|
||||
**Features:**
|
||||
|
||||
- 4 Status-Typen mit passenden Farben
|
||||
- Animiertes Icon (Pulse Animation)
|
||||
- Responsive Icon-Größe
|
||||
- Slot-basierte Inhalte
|
||||
|
||||
**Verwendung:**
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import ExperimentaStatusMessage from './ExperimentaStatusMessage.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Success Message -->
|
||||
<ExperimentaStatusMessage type="success" title="Verlängerung erfolgreich!">
|
||||
<p>Ihre Jahreskarte wurde erfolgreich verlängert.</p>
|
||||
</ExperimentaStatusMessage>
|
||||
|
||||
<!-- Error Message -->
|
||||
<ExperimentaStatusMessage type="error" title="Ein Fehler ist aufgetreten">
|
||||
<p>Bitte versuchen Sie es erneut.</p>
|
||||
</ExperimentaStatusMessage>
|
||||
|
||||
<!-- Custom Icon -->
|
||||
<ExperimentaStatusMessage type="warning" title="Achtung" icon="⚠">
|
||||
<p>Dies ist eine Warnung.</p>
|
||||
</ExperimentaStatusMessage>
|
||||
|
||||
<!-- Custom Title Slot -->
|
||||
<ExperimentaStatusMessage type="info">
|
||||
<template #title>
|
||||
<span>Information</span>
|
||||
</template>
|
||||
<p>Hier ist eine Info.</p>
|
||||
</ExperimentaStatusMessage>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. ExperimentaLogo.vue
|
||||
|
||||
Offizielles experimenta X-Logo mit Farbverläufen.
|
||||
|
||||
**Features:**
|
||||
|
||||
- SVG-basiert (skalierbar, scharf)
|
||||
- 3 Größen (Small, Medium, Large)
|
||||
- Responsive (passt sich automatisch an)
|
||||
- Optional als Link verwendbar
|
||||
- Hover-Animation
|
||||
|
||||
**Verwendung:**
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import ExperimentaLogo from './ExperimentaLogo.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Logo (default: large) -->
|
||||
<ExperimentaLogo />
|
||||
|
||||
<!-- Logo als Link -->
|
||||
<ExperimentaLogo href="https://www.experimenta.science" target="_blank" />
|
||||
|
||||
<!-- Small Logo -->
|
||||
<ExperimentaLogo size="small" />
|
||||
|
||||
<!-- Medium Logo -->
|
||||
<ExperimentaLogo size="medium" />
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration in Nuxt 4
|
||||
|
||||
### Option 1: Komponenten in `components/` verschieben
|
||||
|
||||
Kopiere die gewünschten Komponenten nach `components/`:
|
||||
|
||||
```bash
|
||||
cp docs/design-examples/components/ExperimentaButton.vue components/
|
||||
```
|
||||
|
||||
Nuxt erkennt sie automatisch (Auto-Imports):
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<ExperimentaButton>Click me</ExperimentaButton>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Option 2: Als Composable verwenden
|
||||
|
||||
Erstelle eine Composable-Funktion in `composables/useExperimenta.ts`:
|
||||
|
||||
```typescript
|
||||
export function useExperimenta() {
|
||||
return {
|
||||
// Export component references
|
||||
ExperimentaButton: () => import('@/docs/design-examples/components/ExperimentaButton.vue'),
|
||||
ExperimentaCard: () => import('@/docs/design-examples/components/ExperimentaCard.vue'),
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Anpassungen & Erweiterungen
|
||||
|
||||
### shadcn-nuxt Integration
|
||||
|
||||
Diese Komponenten können **shadcn-nuxt Komponenten ersetzen** oder ergänzen:
|
||||
|
||||
```vue
|
||||
<!-- Statt shadcn Button: -->
|
||||
<Button>Click me</Button>
|
||||
|
||||
<!-- Verwende experimenta Button: -->
|
||||
<ExperimentaButton>Click me</ExperimentaButton>
|
||||
```
|
||||
|
||||
### Tailwind Klassen verwenden
|
||||
|
||||
Alle Komponenten verwenden Tailwind CSS Utilities. Du kannst sie anpassen:
|
||||
|
||||
```vue
|
||||
<ExperimentaButton class="mt-8">
|
||||
Custom Margin
|
||||
</ExperimentaButton>
|
||||
```
|
||||
|
||||
### Custom Variants hinzufügen
|
||||
|
||||
Beispiel: Eine neue Button-Variant hinzufügen:
|
||||
|
||||
```vue
|
||||
<!-- In ExperimentaButton.vue -->
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
variant?: 'primary' | 'secondary' | 'tertiary' // Neu: tertiary
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Neue Variant definieren */
|
||||
.btn-tertiary {
|
||||
@apply bg-info text-white;
|
||||
}
|
||||
|
||||
.btn-tertiary:hover {
|
||||
@apply bg-info-dark;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## TypeScript Support
|
||||
|
||||
Alle Komponenten sind **TypeScript-ready** mit vollständigen Prop-Definitionen.
|
||||
|
||||
Beispiel für Type-Safe Usage:
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import ExperimentaButton from './ExperimentaButton.vue'
|
||||
|
||||
function handleClick(event: MouseEvent) {
|
||||
console.log('Button clicked', event)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ExperimentaButton variant="primary" size="large" :disabled="false" @click="handleClick">
|
||||
Click me
|
||||
</ExperimentaButton>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Accessibility (A11y)
|
||||
|
||||
Alle Komponenten folgen **WCAG 2.1 AA Standards**:
|
||||
|
||||
- ✅ **Keyboard Navigation** (Tab, Enter, Space)
|
||||
- ✅ **Focus Indicators** (sichtbarer Focus-Ring)
|
||||
- ✅ **ARIA Labels** (Screen Reader Support)
|
||||
- ✅ **Color Contrast** (mindestens 4.5:1 Ratio)
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
Beispiel für Vitest Unit Tests:
|
||||
|
||||
```typescript
|
||||
// ExperimentaButton.spec.ts
|
||||
import { mount } from '@vue/test-utils'
|
||||
import ExperimentaButton from './ExperimentaButton.vue'
|
||||
|
||||
describe('ExperimentaButton', () => {
|
||||
it('renders primary button by default', () => {
|
||||
const wrapper = mount(ExperimentaButton, {
|
||||
slots: { default: 'Click me' },
|
||||
})
|
||||
|
||||
expect(wrapper.find('.btn-primary').exists()).toBe(true)
|
||||
expect(wrapper.text()).toBe('Click me')
|
||||
})
|
||||
|
||||
it('emits click event', async () => {
|
||||
const wrapper = mount(ExperimentaButton)
|
||||
await wrapper.trigger('click')
|
||||
|
||||
expect(wrapper.emitted('click')).toBeTruthy()
|
||||
})
|
||||
|
||||
it('renders as link when href is provided', () => {
|
||||
const wrapper = mount(ExperimentaButton, {
|
||||
props: { href: 'https://example.com' },
|
||||
})
|
||||
|
||||
expect(wrapper.element.tagName).toBe('A')
|
||||
expect(wrapper.attributes('href')).toBe('https://example.com')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Storybook Integration (Optional)
|
||||
|
||||
Erstelle Stories für visuelle Dokumentation:
|
||||
|
||||
```typescript
|
||||
// ExperimentaButton.stories.ts
|
||||
import type { Meta, StoryObj } from '@storybook/vue3'
|
||||
import ExperimentaButton from './ExperimentaButton.vue'
|
||||
|
||||
const meta: Meta<typeof ExperimentaButton> = {
|
||||
title: 'Components/ExperimentaButton',
|
||||
component: ExperimentaButton,
|
||||
tags: ['autodocs'],
|
||||
}
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof ExperimentaButton>
|
||||
|
||||
export const Primary: Story = {
|
||||
args: {
|
||||
variant: 'primary',
|
||||
},
|
||||
render: (args) => ({
|
||||
components: { ExperimentaButton },
|
||||
setup() {
|
||||
return { args }
|
||||
},
|
||||
template: '<ExperimentaButton v-bind="args">Click me</ExperimentaButton>',
|
||||
}),
|
||||
}
|
||||
|
||||
export const Secondary: Story = {
|
||||
args: {
|
||||
variant: 'secondary',
|
||||
},
|
||||
render: (args) => ({
|
||||
components: { ExperimentaButton },
|
||||
setup() {
|
||||
return { args }
|
||||
},
|
||||
template: '<ExperimentaButton v-bind="args">Cancel</ExperimentaButton>',
|
||||
}),
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Weitere Ressourcen
|
||||
|
||||
- **Design System Dokumentation**: `docs/DESIGN_SYSTEM.md`
|
||||
- **Tailwind Config**: `tailwind.config.ts`
|
||||
- **CSS Custom Properties**: `assets/css/tailwind.css`
|
||||
- **Design-Vorlagen**: `design-examples/*.html`
|
||||
|
||||
---
|
||||
|
||||
**Fragen oder Feedback?** → docs@experimenta.science
|
||||
Reference in New Issue
Block a user