ADD: Components
Teaser, Card
This commit is contained in:
parent
8943512328
commit
c8e4bf4a14
8 changed files with 127 additions and 48 deletions
|
@ -17,22 +17,6 @@
|
||||||
& .bottom {
|
& .bottom {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
& .image-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
& .skill-card {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&.reverse {
|
|
||||||
flex-direction: row-reverse;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (width <= 1601px) {
|
@media (width <= 1601px) {
|
||||||
|
|
43
app/assets/css/teaser.css
Normal file
43
app/assets/css/teaser.css
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
.Teaser {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
& .image-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.right {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media (width <= 1601px) {
|
||||||
|
.Teaser {
|
||||||
|
flex: 1 0 300px;
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
justify-content: start;
|
||||||
|
|
||||||
|
&.right {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
& h3 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .text-container,
|
||||||
|
& .text-container main {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .image-container img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
app/components/Card.vue
Normal file
15
app/components/Card.vue
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<template>
|
||||||
|
<article class="z-2 card flex-col gap-sm">
|
||||||
|
<component :is="titleTag ?? 'h3'">{{ title }}</component>
|
||||||
|
<slot />
|
||||||
|
</article>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
type Props = {
|
||||||
|
title : string
|
||||||
|
titleTag ?: 'strong' | 'h3'
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
</script>
|
|
@ -3,20 +3,18 @@
|
||||||
<h2>Meine Expertise.</h2>
|
<h2>Meine Expertise.</h2>
|
||||||
<h3>Dies sind meine <span class="highlight">Spezialgebiete</span> - aber ich bin flexibel!</h3>
|
<h3>Dies sind meine <span class="highlight">Spezialgebiete</span> - aber ich bin flexibel!</h3>
|
||||||
<div class="skill-list margin-top gap-default">
|
<div class="skill-list margin-top gap-default">
|
||||||
<article class="z-2 card flex-col gap-sm" v-for="skill in skills">
|
<Card v-for="skill in skills" :title="skill.title" titleTag="h3">
|
||||||
<h3>{{skill.title}}</h3>
|
|
||||||
<p v-for="(t, i) in skill.text" :class="[i === skill.text.length - 1 && 'bold']">{{t}}</p>
|
<p v-for="(t, i) in skill.text" :class="[i === skill.text.length - 1 && 'bold']">{{t}}</p>
|
||||||
</article>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
<article class="tech-list z-2 card flex-col gap-default margin-top">
|
<Card title="Technologien" titleTag="h3" class="margin-top">
|
||||||
<h3>Technologien</h3>
|
|
||||||
<p>Neben den klassischen Webentwicklungsstandards JavaScript, HTML und CSS biete ich außerdem folgende Technologien.</p>
|
<p>Neben den klassischen Webentwicklungsstandards JavaScript, HTML und CSS biete ich außerdem folgende Technologien.</p>
|
||||||
<ul class="gap-default">
|
<ul class="gap-default margin-top-small">
|
||||||
<li v-for="tech in technologies">
|
<li v-for="tech in technologies">
|
||||||
<Technology v-bind="tech" size="l"/>
|
<Technology v-bind="tech" size="l"/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</article>
|
</Card>
|
||||||
<div class="bottom flex-col margin-top gap-default">
|
<div class="bottom flex-col margin-top gap-default">
|
||||||
<h3>Manche von euch haben hier sicher kein Wort verstanden.</h3>
|
<h3>Manche von euch haben hier sicher kein Wort verstanden.</h3>
|
||||||
<Button href="#skills_easy" class="cta">
|
<Button href="#skills_easy" class="cta">
|
||||||
|
|
|
@ -3,25 +3,13 @@
|
||||||
<h2>Jetzt mal ganz konkret.</h2>
|
<h2>Jetzt mal ganz konkret.</h2>
|
||||||
<h3>In diesem Abschnitt ganz <span class="highlight">ohne Technik-Blabla</span>.</h3>
|
<h3>In diesem Abschnitt ganz <span class="highlight">ohne Technik-Blabla</span>.</h3>
|
||||||
<div class="skill-container flex-col margin-top gap-default">
|
<div class="skill-container flex-col margin-top gap-default">
|
||||||
<article class="skill-card z-2 card gap-default" v-for="(skill, skillIndex) in skills" :class="[skillIndex % 2 && 'reverse']">
|
<Teaser
|
||||||
<div class="image-container">
|
v-for="(skill, skillIndex) in skills"
|
||||||
<img
|
:title="skill.title"
|
||||||
loading="lazy"
|
:image="{ path: '/img/explanations/', name : skill.img, position: skillIndex % 2 ? 'right' : 'left'}"
|
||||||
width="550"
|
>
|
||||||
height="350"
|
|
||||||
:srcset="[skill.img('1x', true), skill.img('2x', true), skill.img('3x', true)].join(', ')"
|
|
||||||
:src="skill.img('1x', false)"
|
|
||||||
aria-hidden="true"
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="text-container flex-col gap-default">
|
|
||||||
<h3>{{skill.title}}</h3>
|
|
||||||
<main class="flex-col gap-sm">
|
|
||||||
<RichText :elements="skill.text" />
|
<RichText :elements="skill.text" />
|
||||||
</main>
|
</Teaser>
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="bottom flex-col margin-top gap-default">
|
<div class="bottom flex-col margin-top gap-default">
|
||||||
<h3>Verwirkliche jetzt dein Webprojekt.</h3>
|
<h3>Verwirkliche jetzt dein Webprojekt.</h3>
|
||||||
|
@ -37,7 +25,7 @@
|
||||||
import type { RichText } from '@/components/RichText/Types'
|
import type { RichText } from '@/components/RichText/Types'
|
||||||
|
|
||||||
type Skill = {
|
type Skill = {
|
||||||
img: Function
|
img: string
|
||||||
title: string
|
title: string
|
||||||
text: RichText[]
|
text: RichText[]
|
||||||
}
|
}
|
||||||
|
@ -46,7 +34,7 @@ const getExplanationImage = (img : string) => getImage('/img/explanations/', img
|
||||||
|
|
||||||
const skills : Skill[] = [
|
const skills : Skill[] = [
|
||||||
{
|
{
|
||||||
img: getExplanationImage('components'),
|
img: 'components',
|
||||||
title: 'Das, was du sehen kannst',
|
title: 'Das, was du sehen kannst',
|
||||||
text: [
|
text: [
|
||||||
{
|
{
|
||||||
|
@ -106,7 +94,7 @@ const skills : Skill[] = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}, {
|
}, {
|
||||||
img: getExplanationImage('cms'),
|
img: 'cms',
|
||||||
title: 'Da, wo du eintragen kannst',
|
title: 'Da, wo du eintragen kannst',
|
||||||
text: [
|
text: [
|
||||||
{
|
{
|
||||||
|
@ -207,7 +195,7 @@ const skills : Skill[] = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}, {
|
}, {
|
||||||
img: getExplanationImage('result'),
|
img: 'result',
|
||||||
title: 'Was dabei am Ende rauskommt',
|
title: 'Was dabei am Ende rauskommt',
|
||||||
text: [
|
text: [
|
||||||
{
|
{
|
||||||
|
|
42
app/components/Teaser.vue
Normal file
42
app/components/Teaser.vue
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<template>
|
||||||
|
<article
|
||||||
|
class="Teaser z-2 card flex-col gap-sm"
|
||||||
|
:class="[image.position ?? 'left']"
|
||||||
|
>
|
||||||
|
<div class="image-container">
|
||||||
|
<img
|
||||||
|
loading="lazy"
|
||||||
|
width="550"
|
||||||
|
height="350"
|
||||||
|
:srcset="imageSet.join(', ')"
|
||||||
|
:src="initialImage"
|
||||||
|
aria-hidden="true"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="text-container flex-col gap-default">
|
||||||
|
<h3>{{title}}</h3>
|
||||||
|
<main class="flex-col gap-sm">
|
||||||
|
<slot />
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { getImageSet, getInitialImage } from '../utils/image'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
title : string
|
||||||
|
image : {
|
||||||
|
path : string
|
||||||
|
name : string
|
||||||
|
position ?: 'left' | 'right'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { image } = defineProps<Props>()
|
||||||
|
|
||||||
|
const imageSet = getImageSet(image.path, image.name)
|
||||||
|
const initialImage = getInitialImage(image.path, image.name)
|
||||||
|
</script>
|
|
@ -1,3 +1,11 @@
|
||||||
|
type Size = '1x' | '2x' | '3x'
|
||||||
|
const allSizes : Size[] = ['1x', '2x', '3x']
|
||||||
|
|
||||||
export const getImage =
|
export const getImage =
|
||||||
(path: string, img: string) => (size: "1x" | "2x" | "3x", set: boolean) =>
|
(path: string, img: string) => (size: Size, set: boolean) =>
|
||||||
`${path}${img}@${size}.webp${set ? ` ${size}` : ""}`;
|
`${path}${img}@${size}.webp${set ? ` ${size}` : ''}`;
|
||||||
|
|
||||||
|
export const getImageSet = (path: string, img: string) =>
|
||||||
|
allSizes.map((size) => getImage(path, img)(size, true))
|
||||||
|
|
||||||
|
export const getInitialImage = (path: string, img: string) => getImage(path, img)('1x', false)
|
|
@ -32,6 +32,7 @@ export default defineNuxtConfig({
|
||||||
'~/assets/css/button.css',
|
'~/assets/css/button.css',
|
||||||
'~/assets/css/spoiler.css',
|
'~/assets/css/spoiler.css',
|
||||||
'~/assets/css/burger.css',
|
'~/assets/css/burger.css',
|
||||||
|
'~/assets/css/teaser.css',
|
||||||
],
|
],
|
||||||
|
|
||||||
postcss: {
|
postcss: {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue