first draft
This commit is contained in:
parent
8a5a4bb40e
commit
f8b6b79ce1
33 changed files with 1913 additions and 936 deletions
|
|
@ -41,6 +41,8 @@ onMount(()=>{
|
|||
backgroundAlpha: 0,
|
||||
view: canvas,
|
||||
});
|
||||
|
||||
PIXI.Filter.defaultResolution = 2;
|
||||
|
||||
//for debugging but Typescript has an issue with this:
|
||||
(globalThis as any).__PIXI_APP__ = app;
|
||||
|
|
@ -64,24 +66,26 @@ onMount(()=>{
|
|||
bulgegroup.addChild(bulgebg);
|
||||
|
||||
let center = [0.5, 0.5];
|
||||
|
||||
let bulgefilter = new BulgePinchFilter();
|
||||
bulgefilter.radius = is_landscape ? xFrac(0.5) : xFrac(0.55);
|
||||
bulgefilter.strength = 0.5;
|
||||
bulgefilter.center = is_landscape ? center : [0.5, 0];
|
||||
bulgefilter.resolution = 2;
|
||||
// bulgefilter.resolution = 2;
|
||||
|
||||
let twistfilter = new TwistFilter();
|
||||
twistfilter.angle = 0;
|
||||
twistfilter.radius = window.innerWidth/2;
|
||||
twistfilter.offset = new PIXI.Point(window.innerWidth/2, window.innerHeight/2);
|
||||
twistfilter.resolution = 2;
|
||||
twistfilter.radius = is_landscape ? window.innerWidth/4 : window.innerWidth/2;
|
||||
twistfilter.offset = new PIXI.Point(window.innerWidth/2, window.innerHeight/3);
|
||||
// twistfilter.resolution = 2;
|
||||
|
||||
|
||||
bulgegroup.filters = [bulgefilter, twistfilter];
|
||||
|
||||
gsap.to(twistfilter, {
|
||||
angle: .75,
|
||||
angle: -1.33,
|
||||
duration: 1,
|
||||
ease: 'elastic.out',
|
||||
ease: 'elastic.out(2, 0.4)',
|
||||
delay: 2,
|
||||
onComplete: () => {
|
||||
is_twisted = true;
|
||||
|
|
@ -174,13 +178,15 @@ onMount(()=>{
|
|||
const pointerXfrac = pointerX - 0.5;
|
||||
const pointerYfrac = pointerY - 0.5;
|
||||
|
||||
gsap.to(mouse, {
|
||||
duration: 0.5,
|
||||
ease: 'power3.out',
|
||||
overwrite: true,
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
})
|
||||
if (is_twisted){
|
||||
gsap.to(mouse, {
|
||||
duration: 0.5,
|
||||
ease: 'power3.out',
|
||||
overwrite: true,
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
})
|
||||
}
|
||||
|
||||
gsap.to(tween, {
|
||||
duration: .5,
|
||||
|
|
@ -189,7 +195,7 @@ onMount(()=>{
|
|||
x: 0.5 + pointerXfrac/2,
|
||||
y: 0.5 + pointerYfrac/6,
|
||||
})
|
||||
})
|
||||
}, { passive: true })
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -202,12 +208,16 @@ onMount(()=>{
|
|||
elapsed += delta;
|
||||
if (!is_landscape) {
|
||||
// bulgefilter.center = [(tween.x + Math.sin(elapsed/200)/20 ),(tween.y + Math.cos(elapsed/200)/20 )];
|
||||
bulgefilter.center = [(0.5 + Math.sin(elapsed/200)/20 ),(0.4 + Math.cos(elapsed/200)/20 )];
|
||||
twistfilter.offset = new PIXI.Point( twistfilter.offset.x + Math.sin(elapsed/100) - Math.sin(elapsed/100) * 2, twistfilter.offset.y );
|
||||
let movingCenter = [(0.5 + Math.sin(elapsed/200)/20 ),(0.33 + Math.cos(elapsed/200)/20 )];
|
||||
bulgefilter.center = movingCenter;
|
||||
twistfilter.offset = new PIXI.Point(
|
||||
window.innerWidth * movingCenter[0] ,
|
||||
window.innerHeight * movingCenter[1]
|
||||
);
|
||||
// bulgefilter.center = [0.5, 0.25];
|
||||
} else {
|
||||
bulgefilter.center = [tween.x, tween.y];
|
||||
twistfilter.offset = new PIXI.Point(mouse.x, mouse.y);
|
||||
is_twisted ? twistfilter.offset = new PIXI.Point(mouse.x, window.innerHeight/3) : null;
|
||||
}
|
||||
updateImgs();
|
||||
updateText();
|
||||
|
|
|
|||
|
|
@ -11,6 +11,19 @@
|
|||
</header>
|
||||
|
||||
<style lang="scss">
|
||||
@media screen and (max-width: 767px) {
|
||||
.header-nav:before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
height: 100px;
|
||||
pointer-events: none;
|
||||
background: linear-gradient(0deg, var(--color-bg) 10%, rgba(255, 255, 255, 0) 100%);
|
||||
z-index: 3;
|
||||
}
|
||||
}
|
||||
.logo {
|
||||
height: 36px;
|
||||
width: auto;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import * as PIXI from 'pixi.js';
|
||||
import { BulgePinchFilter } from 'pixi-filters';
|
||||
import { BulgePinchFilter, TwistFilter } from 'pixi-filters';
|
||||
import gsap from 'gsap';
|
||||
import ScrollTrigger from 'gsap/dist/ScrollTrigger';
|
||||
import SplitText from 'gsap/dist/SplitText';
|
||||
|
|
@ -41,6 +41,8 @@ onMount(()=>{
|
|||
view: canvas,
|
||||
});
|
||||
|
||||
PIXI.Filter.defaultResolution = 2;
|
||||
|
||||
//for debugging but Typescript has an issue with this:
|
||||
(globalThis as any).__PIXI_APP__ = app;
|
||||
|
||||
|
|
@ -64,10 +66,9 @@ onMount(()=>{
|
|||
|
||||
let center = [0.5, 0.5];
|
||||
let bulgefilter = new BulgePinchFilter();
|
||||
bulgefilter.radius = is_landscape ? xFrac(0.5) : xFrac(0.55);
|
||||
bulgefilter.strength = 0.5;
|
||||
bulgefilter.radius = is_landscape ? xFrac(0.5) : xFrac(0.5);
|
||||
bulgefilter.strength = 0;
|
||||
bulgefilter.center = center;
|
||||
bulgefilter.resolution = 2;
|
||||
|
||||
bulgegroup.filters = [bulgefilter];
|
||||
|
||||
|
|
@ -79,11 +80,6 @@ onMount(()=>{
|
|||
duration: .5,
|
||||
ease: 'power4.inOut',
|
||||
}, 1.5)
|
||||
// introTl.to(bulgefilter, {
|
||||
// strength: 0.75,
|
||||
// duration: .75,
|
||||
// ease: 'power4.inOut',
|
||||
// })
|
||||
introTl.to(bulgefilter, {
|
||||
strength: 0.5,
|
||||
duration: 1.25,
|
||||
|
|
@ -192,7 +188,7 @@ onMount(()=>{
|
|||
y: 0.5 + pointerYfrac/2,
|
||||
})
|
||||
|
||||
})
|
||||
}, { passive: true })
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -204,10 +200,11 @@ onMount(()=>{
|
|||
app.ticker.add((delta) => {
|
||||
elapsed += delta;
|
||||
if (is_portrait) {
|
||||
bulgefilter.center = [(0.5 + Math.sin(elapsed/200)/20 ),(0.45 + Math.cos(elapsed/200)/20 )];
|
||||
// bulgefilter.center = [0.5, 0.45];
|
||||
bulgefilter.center = [(0.5 + Math.sin(elapsed/200)/40 ),(0.45 + Math.cos(elapsed/200)/20 )];
|
||||
// bulgefilter.center = [0.5, 0.5];
|
||||
} else {
|
||||
bulgefilter.center = [tween.x, tween.y];
|
||||
bulgefilter.center = [(tween.x + Math.sin(elapsed/200)/40 ),(tween.y + Math.cos(elapsed/200)/20 )];
|
||||
// bulgefilter.center = [tween.x, tween.y];
|
||||
}
|
||||
updateImgs();
|
||||
updateText();
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@
|
|||
checkbox ? checkbox.checked = false : null;
|
||||
gsap.to('.content > *', {duration: 0.5, y: '100%', autoAlpha: 1, ease: 'power4.out'});
|
||||
gsap.to('#nav', {duration: 0.5, autoAlpha: 1, y: '100%', ease: 'power4.out', delay: 0.1});
|
||||
gsap.to('nav a', {duration: 0.5, autoAlpha: 1, y: 200, stagger: 0.05, ease: 'power4.out' , delay: 0.2});
|
||||
})
|
||||
gsap.to('nav a', {duration: 0.5, autoAlpha: 1, y: 200, stagger: 0.025, ease: 'power4.out' , delay: 0.1});
|
||||
}, { passive: true })
|
||||
});
|
||||
|
||||
//Toggle menu
|
||||
|
|
@ -33,11 +33,11 @@
|
|||
if (checkbox.checked) {
|
||||
gsap.to('.content > *', {duration: 0.75, y: -200, autoAlpha: 0.5, ease: 'power2.inOut'});
|
||||
gsap.to('#nav', {duration: 0.5, autoAlpha: 1, y: 0, ease: 'power4.out', delay: 0.1});
|
||||
gsap.to('nav a', {duration: 0.5, autoAlpha: 1, y: 0, stagger: 0.1, ease: 'power4.out' , delay: 0.2});
|
||||
gsap.to('nav a', {duration: 0.5, autoAlpha: 1, y: 0, stagger: 0.1, ease: 'power4.out' , delay: 0.1});
|
||||
} else {
|
||||
gsap.to('.content > *', {duration: 0.5, y: 0, autoAlpha: 1, ease: 'power4.out'});
|
||||
gsap.to('#nav', {duration: 0.5, autoAlpha: 1, y: '100%', ease: 'power4.out', delay: 0.1});
|
||||
gsap.to('nav a', {duration: 0.75, autoAlpha: 1, y: 200, stagger: 0.05, ease: 'power4.out' , delay: 0.2});
|
||||
gsap.to('nav a', {duration: 0.75, autoAlpha: 1, y: 200, stagger: 0.025, ease: 'power4.out' , delay: 0.1});
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -53,8 +53,11 @@
|
|||
</label>
|
||||
<nav id="nav">
|
||||
<a href="/" class="navlink {$page.route.id === '/' ? 'current' : ''}">Home</a>
|
||||
<a href="/about" class="navlink {$page.route.id === '/about' ? 'current' : ''}">About</a>
|
||||
<a href="/work" class="navlink {$page.route.id === '/work' ? 'current' : ''}">Work</a>
|
||||
<a href="/work" class="navlink {$page.route.id?.includes('/work') ? 'current' : ''}">Work</a>
|
||||
<a href="/service" class="navlink {$page.route.id === '/service' ? 'current' : ''}">Services</a>
|
||||
<!-- <a href="/about" class="navlink {$page.route.id === '/about' ? 'current' : ''}">About</a> -->
|
||||
<a href="/contact" class="navlink {$page.route.id === '/contact' ? 'current' : ''}">Contact</a>
|
||||
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
|
@ -111,8 +114,8 @@
|
|||
}
|
||||
#nav a {
|
||||
display: block;
|
||||
line-height: 1.3;
|
||||
font-size: 3em;
|
||||
line-height: 1;
|
||||
font-size: 2.5em;
|
||||
font-weight: 800;
|
||||
font-style: italic;
|
||||
text-transform: lowercase;
|
||||
|
|
@ -120,7 +123,7 @@
|
|||
color: var(--color-text);
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
font-size: 5.5em;
|
||||
font-size: 4.5em;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
|
|
@ -128,7 +131,8 @@
|
|||
}
|
||||
|
||||
&.current {
|
||||
text-decoration: underline;
|
||||
// text-decoration: underline;
|
||||
color: var(--color-highlight);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -77,7 +77,9 @@ onMount( () => {
|
|||
bulgefilter.radius = is_landscape ? xFrac(0.5) : xFrac(0.6);
|
||||
bulgefilter.strength = is_landscape ? bulgeFactor : bulgeFactor * 1.25;
|
||||
bulgefilter.center = center;
|
||||
bulgefilter.resolution = 2;
|
||||
// bulgefilter.resolution = 2;
|
||||
|
||||
PIXI.Filter.defaultResolution = 2;
|
||||
|
||||
app.stage.filters = [bulgefilter];
|
||||
|
||||
|
|
@ -208,7 +210,7 @@ onMount( () => {
|
|||
x: pointerX,
|
||||
y: pointerY,
|
||||
})
|
||||
})
|
||||
}, { passive: true })
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
:root {
|
||||
--spacing-outer: 5vw;
|
||||
--spacing-nav: 5vw;
|
||||
--color-bg: #2085e3;
|
||||
--color-text: #ffe9e6;
|
||||
// --color-bg: hsl(202, 58%, 39%);
|
||||
--color-bg: hsl(0, 60%, 53%);
|
||||
--color-text: #ffead9;
|
||||
--color-highlight: #1d0c12;
|
||||
// --color-bg: #000000;
|
||||
// --color-text: #FFFFFF;
|
||||
|
|
@ -15,6 +16,7 @@
|
|||
--spacing-nav: 2.5vw;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
cursor: url('/cursor.svg'), auto;
|
||||
}
|
||||
|
|
@ -52,13 +54,13 @@ h1, h2, h3, h4, h5 {
|
|||
letter-spacing: -0.025em;
|
||||
}
|
||||
h1 {
|
||||
font-size: 3.5em;
|
||||
font-size: 2.5em;
|
||||
}
|
||||
h2 {
|
||||
font-size: 2em;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5em;
|
||||
font-size: 1.25em;
|
||||
}
|
||||
ul, ol {
|
||||
padding-left: 0;
|
||||
|
|
@ -73,6 +75,12 @@ p, li {
|
|||
}
|
||||
a {
|
||||
color: inherit;
|
||||
text-underline-offset: 0.25em;
|
||||
text-decoration-thickness: 0.066em;
|
||||
transition: all .3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
&:hover {
|
||||
text-underline-offset: 0.1em;
|
||||
}
|
||||
}
|
||||
.cta {
|
||||
font-size: clamp(20px, 1.6vw, 1.6vw);
|
||||
|
|
@ -95,8 +103,8 @@ a {
|
|||
transition: all .3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
|
||||
&:hover {
|
||||
color: var(--color-bg);
|
||||
@media screen and (min-width: 768px) {
|
||||
// font-size: 1.05em;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
}
|
||||
|
|
@ -116,6 +124,7 @@ a {
|
|||
transition: all .3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
&:hover:before {
|
||||
background-color: var(--color-text);
|
||||
width: calc(100% + 0.25em);
|
||||
height: calc(100% + 10px);
|
||||
left: -0.125em;
|
||||
|
|
@ -125,10 +134,17 @@ a {
|
|||
&.button--primary {
|
||||
color: var(--color-text);
|
||||
|
||||
&:hover {
|
||||
color: var(--color-highlight);
|
||||
}
|
||||
&:before {
|
||||
background-color: var(--color-highlight);
|
||||
border: 2px solid var(--color-highlight);
|
||||
}
|
||||
&:hover:before {
|
||||
background-color: var(--color-text);
|
||||
border: 2px solid var(--color-text);
|
||||
}
|
||||
}
|
||||
&.button--xl {
|
||||
font-size: clamp(24px, 3.2vw, 3.2vw);
|
||||
|
|
@ -164,4 +180,9 @@ a {
|
|||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.hide-on-mobile {
|
||||
@media screen and (orientation: portrait) and (max-width: 767px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ export default function createCanvasText( element: HTMLElement, stage: PIXI.Con
|
|||
const elemAlignment = elemStyles.getPropertyValue('text-align') || 'left';
|
||||
const elemPosition = elem.getBoundingClientRect();
|
||||
const elemTextTransform = elemStyles.getPropertyValue('text-transform') || 'none';
|
||||
|
||||
if (elemTextTransform === 'uppercase') {
|
||||
elem.textContent = elem.textContent?.toUpperCase() as string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
import '$lib/styles/global.scss';
|
||||
import Header from '$lib/components/Header.svelte';
|
||||
import Loader from '$lib/components/Loader.svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { navigating } from '$app/stores';
|
||||
export let data;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
import { onMount } from 'svelte';
|
||||
import { gsap } from 'gsap';
|
||||
import ScrollTrigger from 'gsap/dist/ScrollTrigger';
|
||||
import ScrollToPlugin from 'gsap/dist/ScrollToPlugin';
|
||||
import { goto } from '$app/navigation';
|
||||
import SplitText from 'gsap/dist/SplitText';
|
||||
import HomeIlluDev from '$lib/components/HomeIlluDev.svelte';
|
||||
import HomeIlluShapes from '$lib/components/HomeIlluShapes.svelte';
|
||||
|
|
@ -16,7 +18,7 @@
|
|||
|
||||
mounted = true;
|
||||
|
||||
gsap.registerPlugin( ScrollTrigger, SplitText );
|
||||
gsap.registerPlugin( ScrollTrigger, ScrollToPlugin, SplitText );
|
||||
|
||||
const sections = document.querySelectorAll('section');
|
||||
|
||||
|
|
@ -50,8 +52,8 @@
|
|||
gsap.to(split.words, { duration: 1, yPercent: 0, opacity: 1, stagger: 0.05, ease: 'power4.out',
|
||||
scrollTrigger: { trigger: split.words, start: 'top 90%', end: 'bottom 70%', scrub: false }
|
||||
})
|
||||
if (section.querySelector('.toCanvas')) {
|
||||
let splitp = new SplitText(section.querySelectorAll('.toCanvas'), { type: 'lines', linesClass: 'lineChildren' });
|
||||
if (section.querySelector('p')) {
|
||||
let splitp = new SplitText(section.querySelectorAll('p'), { type: 'lines', linesClass: 'lineChildren' });
|
||||
gsap.set(splitp.lines, { transformOrigin: '0% 100%' });
|
||||
gsap.set(splitp.lines, { yPercent: 100, opacity: 0 });
|
||||
gsap.to(splitp.lines, { duration: 1, yPercent: 0, opacity: 1, stagger: 0.05, ease: 'power4.out',
|
||||
|
|
@ -61,9 +63,27 @@
|
|||
}
|
||||
|
||||
})
|
||||
canvasElems = Array.from(document.querySelectorAll('.lineChildren, .wordChildren'));
|
||||
canvasElems = Array.from(document.querySelectorAll('.canvasized .lineChildren, .canvasized .wordChildren'));
|
||||
imgElems = Array.from(document.querySelectorAll('img'));
|
||||
|
||||
sections.forEach(section => {
|
||||
//create a click eventlistener, that will scroll to the next section
|
||||
section.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
//if the cursor is over a link, don't scroll and open the link's href location
|
||||
let target = e.target as HTMLElement;
|
||||
if (target?.closest('a')) {
|
||||
goto(target.closest('a')?.getAttribute('href') || '');
|
||||
return;
|
||||
}
|
||||
|
||||
let nextSection = section.nextElementSibling as HTMLElement;
|
||||
if (nextSection) {
|
||||
gsap.to(window, { duration: 1, scrollTo: nextSection, ease: 'power4.inOut' })
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return () => {
|
||||
gsap.killTweensOf(canvasElems);
|
||||
ScrollTrigger.getAll().forEach( instance => instance.kill() );
|
||||
|
|
@ -75,18 +95,21 @@
|
|||
<Loader />
|
||||
{/if}
|
||||
<article class="scroller">
|
||||
<section class="splash">
|
||||
<h1 class="align-middle">Hallo! I'm Simon. I forge websites that <em>stand out</em> with exemplary beauty.</h1>
|
||||
<section class="canvasized splash">
|
||||
<h1 class="align-middle">I create digital experiences that <em>stand out</em> from the rest.</h1>
|
||||
</section>
|
||||
<section class="intro">
|
||||
<figure class="intro-image">
|
||||
<section class="canvasized introduction">
|
||||
<h2 class="align-middle">My name is Simon, I help my clients reach their audiences using <em>my skills:</em></h2>
|
||||
</section>
|
||||
<section class="dev">
|
||||
<figure class="dev-image">
|
||||
<HomeIlluDev />
|
||||
</figure>
|
||||
<h2>Creative Development</h2>
|
||||
<p class="toCanvas">I fashion exquisitly tailored web experiences for discerning enterprises and their audiences.</p>
|
||||
<p>I create exquisitly tailored web experiences for discerning enterprises and their audiences.</p>
|
||||
<div class="cta">
|
||||
<a href="/service" class="button">My Services</a>
|
||||
<a href="/contact" class="button button--primary">Get in touch!</a>
|
||||
<a href="/service" class="button">Services <span class="hide-on-mobile"> I provide</span></a>
|
||||
<a href="/contact" class="button button--primary">Contact me</a>
|
||||
</div>
|
||||
</section>
|
||||
<section class="design">
|
||||
|
|
@ -94,15 +117,16 @@
|
|||
<HomeIlluShapes />
|
||||
</figure>
|
||||
<h2>Visual Design</h2>
|
||||
<p class="toCanvas">I'm also a seasoned designer, sculpting communication that's wickedly nice, full of jaw-dropping surprises, and utterly delightful.</p>
|
||||
<p>I'm a seasoned designer, with a long list of succesfull projects and happy clients.</p>
|
||||
<div class="cta">
|
||||
<a href="/service" class="button">My Services</a> <a href="/contact" class="button button--primary">Get in touch!</a>
|
||||
<a href="/work" class="button">Work <span class="hide-on-mobile"> I've done</span></a>
|
||||
<a href="/contact" class="button button--primary">Contact me</a>
|
||||
</div>
|
||||
</section>
|
||||
<section class="more">
|
||||
<h2 class="align-middle">I work as a free agent. Both companies and noble causes can <em>enlist my services</em> for a reasonable wage.</h2>
|
||||
<div class="cta">
|
||||
<a href="/contact" class="button button--xl">Enlist my services</a>
|
||||
<section class="canvasized hireme">
|
||||
<h2 class="align-middle">I work as a free agent. You can <em>hire me</em> for your noble enterprise.</h2>
|
||||
<div class="cta" style="text-align: center">
|
||||
<a href="/contact" class="button button--xl button--primary">Contact me</a>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
|
|
@ -125,7 +149,7 @@
|
|||
section {
|
||||
scroll-snap-align: start;
|
||||
box-sizing: border-box;
|
||||
padding: var(--spacing-outer);
|
||||
padding: 0 var(--spacing-outer);
|
||||
min-height: 100svh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
@ -152,13 +176,24 @@
|
|||
padding: var(--spacing-outer) calc(var(--spacing-outer) * 2);
|
||||
}
|
||||
}
|
||||
.intro-image {
|
||||
.introduction {
|
||||
h2 {
|
||||
font-size: 1.53em;
|
||||
letter-spacing: -0.033em;
|
||||
margin-bottom: 0;
|
||||
line-height: .9;
|
||||
}
|
||||
}
|
||||
.dev-image {
|
||||
position:relative;
|
||||
left: 0;
|
||||
text-align: center;
|
||||
display: block;
|
||||
margin: 0 0 -.5em 0;
|
||||
z-index: -2;
|
||||
margin: -2em 0 -.5em 0;
|
||||
@media screen and (min-width: 768px) {
|
||||
margin: -1.5em 0 -.5em 0;
|
||||
}
|
||||
}
|
||||
.design {
|
||||
overflow: hidden;
|
||||
|
|
@ -170,29 +205,28 @@
|
|||
margin: 0 auto;
|
||||
text-align: center;
|
||||
display: block;
|
||||
margin-bottom: -1em;
|
||||
margin: 0 auto;
|
||||
z-index: -2;
|
||||
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
width: 40%;
|
||||
margin: 0 auto .5em auto;
|
||||
width: 45%;
|
||||
}
|
||||
}
|
||||
.more {
|
||||
.hireme {
|
||||
@media screen and (min-width: 768px) {
|
||||
max-width: 95vw;
|
||||
}
|
||||
}
|
||||
.more h2 {
|
||||
margin-top: -2em;
|
||||
margin-bottom: 1em;
|
||||
.hireme h2 {
|
||||
font-size: 1.53em;
|
||||
@media screen and (min-width: 768px) {
|
||||
margin-bottom: 0.5em;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
h1, h2 {
|
||||
line-height: 1.1;
|
||||
visibility: hidden;
|
||||
line-height: .9;
|
||||
margin: 0;
|
||||
-webkit-touch-callout: none; /* Safari */
|
||||
-webkit-user-select: none; /* Chrome */
|
||||
|
|
@ -206,33 +240,30 @@
|
|||
}
|
||||
}
|
||||
h1 {
|
||||
line-height: .9;
|
||||
font-size: 13.25vw;
|
||||
line-height: .85;
|
||||
font-size: 14.75vw;
|
||||
letter-spacing: -0.05em;
|
||||
margin-top: -.5em;
|
||||
letter-spacing: -0.02em;
|
||||
opacity: 0;
|
||||
color: var(--color-highlight);
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
font-size: 9vw;
|
||||
font-size: 11vw;
|
||||
}
|
||||
|
||||
& > em {
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.5em;
|
||||
letter-spacing: -0.033em;
|
||||
margin-bottom: 0;
|
||||
line-height: .9;
|
||||
}
|
||||
.toCanvas {
|
||||
.canvasized h1, .canvasized h2 {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
p {
|
||||
font-size: .66em;
|
||||
margin: 1.25em 0 0.25em 0;
|
||||
-webkit-touch-callout: none; /* Safari */
|
||||
-webkit-user-select: none; /* Chrome */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
||||
user-select: none;
|
||||
margin: 0 0 0.25em 0;
|
||||
@media screen and (min-width: 768px) {
|
||||
font-size: 0.5em;
|
||||
margin: 1em 0 0.25em 0;
|
||||
font-size: 0.66em;
|
||||
}
|
||||
}
|
||||
a {
|
||||
|
|
@ -243,14 +274,13 @@
|
|||
user-select: none;
|
||||
}
|
||||
.cta {
|
||||
margin-top: 0em;
|
||||
margin-top: -.5em;
|
||||
padding-top: 0em;
|
||||
text-align: center;
|
||||
@media screen and (min-width: 768px) {
|
||||
// background-color: var(--color-bg);
|
||||
// border-top: 1px solid var(--color-text);
|
||||
padding-top: 1em;
|
||||
margin-top: 2em;
|
||||
margin-top: 0em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -9,25 +9,25 @@
|
|||
let contactFormVisible = false;
|
||||
let contactFormClickHandler = (e: Event) => {
|
||||
if (contactFormVisible) {
|
||||
gsap.to('.wordChildren', { opacity: 1, yPercent: 0, duration: 1, ease: 'power4.inOut' })
|
||||
gsap.to('.alternatives > *', { opacity: 1, scale: 1, duration: .5, stagger: 0.1, ease: 'power4.inOut' })
|
||||
gsap.to('.wordChildren', { autoAlpha: 1, yPercent: 0, duration: 1, ease: 'power4.inOut' })
|
||||
gsap.to('.alternatives > *', { autoAlpha: 1, scale: 1, duration: .5, stagger: 0.1, ease: 'power4.inOut' })
|
||||
gsap.fromTo('.formwrapper',
|
||||
{ opacity: 1, yPercent: 0 },
|
||||
{ opacity: 0, yPercent: 50, duration: .6, ease: 'power4.inOut', onComplete: ()=>{
|
||||
{ autoAlpha: 1, yPercent: 0 },
|
||||
{ autoAlpha: 0, yPercent: 50, duration: .6, ease: 'power4.inOut', onComplete: () => {
|
||||
gsap.set('.formwrapper', { zIndex: -1 })
|
||||
} }
|
||||
)
|
||||
} else {
|
||||
gsap.to('.wordChildren', { opacity: 1, yPercent: -200, duration: 1, ease: 'power4.inOut' })
|
||||
gsap.to('.alternatives > *', { opacity: 0, scale: 0.5, duration: .5, stagger: 0.1, ease: 'power4.inOut' })
|
||||
gsap.to('.wordChildren', { autoAlpha: 1, yPercent: -100, duration: 1, ease: 'power4.inOut' })
|
||||
gsap.to('.alternatives > *', { autoAlpha: 0, scale: 0.5, duration: .5, stagger: 0.1, ease: 'power4.inOut' })
|
||||
gsap.set('.formwrapper', { zIndex: 2 })
|
||||
gsap.fromTo('.formwrapper',
|
||||
{ opacity: 1, yPercent: 100 },
|
||||
{ opacity: 1, yPercent: 0, duration: 1, ease: 'power4.inOut' }
|
||||
{ autoAlpha: 1, yPercent: 100 },
|
||||
{ autoAlpha: 1, yPercent: 0, duration: 1, ease: 'power4.inOut' }
|
||||
)
|
||||
gsap.fromTo('.formwrapper label > *',
|
||||
{ opacity: 0, yPercent: 100 },
|
||||
{ opacity: 1, yPercent: 0, duration: .5, stagger: 0.0125, ease: 'power4.out', delay: .25}
|
||||
{ autoAlpha: 0, yPercent: 100 },
|
||||
{ autoAlpha: 1, yPercent: 0, duration: .5, stagger: 0.0125, ease: 'power4.out', delay: .25}
|
||||
)
|
||||
}
|
||||
contactFormVisible = !contactFormVisible;
|
||||
|
|
@ -58,30 +58,29 @@
|
|||
|
||||
const introInElem = document.querySelector('.pagewrapper') as HTMLElement;
|
||||
gsap.fromTo(introInElem.querySelectorAll('.wordChildren'), {
|
||||
opacity: 0, yPercent: -100
|
||||
autoAlpha: 0, yPercent: -100
|
||||
}, {
|
||||
opacity: 1, yPercent: 0, duration: 1.5, stagger: 0.025, ease: 'power4.inOut',
|
||||
autoAlpha: 1, yPercent: 0, duration: 1.5, stagger: 0.025, ease: 'power4.inOut',
|
||||
})
|
||||
gsap.fromTo(introInElem?.querySelectorAll('.alternatives p, .alternatives .button'), {
|
||||
opacity: 0, yPercent: 100
|
||||
autoAlpha: 0, yPercent: 100
|
||||
}, {
|
||||
opacity: 1, yPercent: 0, duration: 1, stagger: 0.025, ease: 'power4.inOut',
|
||||
autoAlpha: 1, yPercent: 0, duration: 1, stagger: 0.025, ease: 'power4.inOut',
|
||||
})
|
||||
|
||||
return () => {
|
||||
gsap.killTweensOf('.toCanvas, .wordChildren');
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<div class="pagewrapper">
|
||||
<div class="intro">
|
||||
<h1 class="toCanvas">Life is weird. <em>Let's be weird together.</em></h1>
|
||||
<h1 class="toCanvas">Let's be strange,<br>not strangers.</h1>
|
||||
<div class="alternatives">
|
||||
<p>Choose your flavour of contact:</p>
|
||||
<ul>
|
||||
<li><span class="button" on:click={contactFormClickHandler} on:keydown={contactFormClickHandler} role="button" tabindex="0">Contact form</span></li>
|
||||
<li><span class="button button--primary" on:click={contactFormClickHandler} on:keydown={contactFormClickHandler} role="button" tabindex="0">Contact form</span></li>
|
||||
<li><a class="button" href="mailto:simon@floter.design">Email</a></li>
|
||||
<li><a class="button" href="https://www.linkedin.com/in/floter/">LinkedIn</a></li>
|
||||
</ul>
|
||||
|
|
@ -103,7 +102,7 @@
|
|||
</div>
|
||||
<label for="contact">
|
||||
<!-- <p>Please describe your plight in a few words</p> -->
|
||||
<textarea rows="6" name="contact" id="contact" placeholder="Your business propositions, praise, complaints and/or threats" required />
|
||||
<textarea rows="8" name="contact" id="contact" placeholder="Your business propositions, praise, complaints and/or threats" required />
|
||||
</label>
|
||||
<div class="send">
|
||||
<button class="button button--xl button--primary" type="submit">Send it!</button>
|
||||
|
|
@ -153,6 +152,7 @@
|
|||
background-color: var(--color-bg);
|
||||
z-index: 0;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
overflow-y: scroll;
|
||||
|
||||
--form-maxwidth: 1000px;
|
||||
|
|
@ -193,17 +193,15 @@
|
|||
opacity: 0;
|
||||
}
|
||||
& p {
|
||||
font-size: 1.4em;
|
||||
margin: 0 0 .5em 0;
|
||||
}
|
||||
& ul {
|
||||
margin: 0 var(--spacing-outer);
|
||||
|
||||
// @media screen and (min-width: 768px) {
|
||||
list-style-type: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: .25em;
|
||||
// }
|
||||
list-style-type: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: .25em;
|
||||
}
|
||||
& ul li {
|
||||
display: block;
|
||||
|
|
@ -213,6 +211,10 @@
|
|||
margin: 0.25em 0;
|
||||
display: block;
|
||||
font-size: .9em;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
label {
|
||||
|
|
@ -223,8 +225,8 @@
|
|||
}
|
||||
input[type='text'], input[type='email'], textarea {
|
||||
width: 100%;
|
||||
border: 3px solid var(--color-text);
|
||||
background-color: var(--color-text);
|
||||
border: 0 solid var(--color-text);
|
||||
background-color: var(--color-text);//Overruled by placeholder-shown if no value
|
||||
color: var(--color-bg);
|
||||
border-radius: 4px;
|
||||
font-family: 'Stratos', sans-serif;
|
||||
|
|
@ -242,20 +244,23 @@
|
|||
background-color: var(--color-text);
|
||||
}
|
||||
&:placeholder-shown {
|
||||
background-color: rgba(255, 205, 205, 0);
|
||||
background-color: rgba(255, 255, 225, 0.2);
|
||||
}
|
||||
&:focus:placeholder-shown {
|
||||
background-color: var(--color-text);
|
||||
}
|
||||
&::placeholder {
|
||||
color: var(--color-text);
|
||||
opacity: .7;
|
||||
opacity: .8;
|
||||
}
|
||||
}
|
||||
label p {
|
||||
max-width: var(--form-maxwidth);
|
||||
margin: 0 auto 1em auto;
|
||||
display: block;
|
||||
font-size: .75em;
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
// label p {
|
||||
// max-width: var(--form-maxwidth);
|
||||
// margin: 0 auto 1em auto;
|
||||
// display: block;
|
||||
// font-size: .75em;
|
||||
// margin-bottom: .5em;
|
||||
// }
|
||||
.send {
|
||||
max-width: var(--form-maxwidth);
|
||||
margin: auto;
|
||||
|
|
|
|||
|
|
@ -67,22 +67,23 @@
|
|||
|
||||
<ServiceCanvas />
|
||||
<article>
|
||||
<h1>What I will do to earn a living<br><em>↓ Scroll down</em></h1>
|
||||
<h1>Services I can provide<br><em>↓ Scroll down</em></h1>
|
||||
<div class="services">
|
||||
<section>
|
||||
<h2>Web Development</h2>
|
||||
<h2>Creative Web Development</h2>
|
||||
<div class="service-content">
|
||||
<ul class="bullets">
|
||||
<li><em>Consultation</em> on the best technology solutions for your situation (eg CMS, frontend frameworks, etc)</li>
|
||||
<li><em>Advice</em> on how to enhance the user experience through savvy use of technology</li>
|
||||
<li><em>Development</em> in HTML/JS/CSS and/or whichever framework is best suited to your project</li>
|
||||
<a class="button" href="/contact">Get a quote</a>
|
||||
<a class="button" href="/work">Work examples</a>
|
||||
<a class="button button--primary" href="/contact">Get in touch!</a>
|
||||
</ul>
|
||||
<div class="faqs">
|
||||
<h3>FAQs</h3>
|
||||
<Faq summary="What kind of websites do you create?">
|
||||
<p>I blend professionalism and creativity. My aim is simple: bring your ideas to life. My websites are often fun and bold and full of interesting interaction.</p>
|
||||
<p>Projects include:</p>
|
||||
<Faq summary="What kind of websites <em>do you</em> create?">
|
||||
<p>My primary goal is simple: bring your ideas to life. My websites are often fun, bold and full of interesting interaction.</p>
|
||||
<p>Projects I worked on include:</p>
|
||||
<ul>
|
||||
<li>Marketing websites</li>
|
||||
<li>E-commerce websites</li>
|
||||
|
|
@ -90,26 +91,34 @@
|
|||
<li>Interactive online comics</li>
|
||||
<li>Browser games</li>
|
||||
</ul>
|
||||
|
||||
<p>I center on crafting interfaces that dazzle visually and deliver an instinctive user journey. The essence is to make your users feel at home in the interaction with your content.</p>
|
||||
<p>Should your venture demand a backend touch, like managing databases or Content Management, I'm well-versed in leveraging existing open source solutions. I advocate for savvy fixes, seamlessly tailored to harmonize with your project.</p>
|
||||
</Faq>
|
||||
<Faq summary="What kind of websites do you <em>not</em> create?">
|
||||
<p>As a frontend developer, I'm not your guy for the intricate machinery that powers colossal enterprise web applications. Put simply, I can't whip up an Amazon or a TikTok from the ground up.</p>
|
||||
<Faq summary="What kind of websites <em>do you not</em> create?">
|
||||
<p>As a <em>frontend</em> developer, I'm not your guy for the intricate machinery that powers colossal enterprise web applications. Put simply, I can't whip up an Amazon or a TikTok from the ground up.</p>
|
||||
<p>Moreover, my moral code is steadfast. Requests involving, but not confined to, promoting weapons manufacturers or propagating conspiracy theories won't find a home with me. I must politely decline such propositions.</p>
|
||||
</Faq>
|
||||
<Faq summary="Which technologies and frameworks do you use/support?">
|
||||
<p>A web browser understands only HTML, CSS, and Javascript. Mastery of these is the bedrock for crafting top-notch web applications.</p>
|
||||
<p>Further, I've treaded diverse coding landscapes, navigating the realms of PHP, Perl, and Python. Among the myriad frameworks danced with, names like React, Svelte, Eleventy, Astro, and the esteemed Ruby on Rails echo.</p>
|
||||
<p>I serve as your counsel, steering you toward the optimal solution tailored to your project's idiosyncrasies.</p>
|
||||
<p>Should you possess a standing website, yearning for expansion, toss me the code, and we'll unravel the possibilities together. Not encountered a site I couldn't tame, so far.</p>
|
||||
<p>And, sometimes, the most fitting scaffold is none at all. I crafted some elegant, straightforward websites in the raw embrace of HTML, CSS, and JS.</p>
|
||||
<p>A web browser understands only <em>HTML, CSS, and Javascript.</em> Mastery of these is the bedrock for crafting top-notch web applications.</p>
|
||||
<p>Among the popular modern Javascript frameworks, I have experience with <em>React and Svelte</em>. In terms of meta frameworks I have so far used <em>NextJS, SvelteKit, Eleventy, and Astro.</em></p>
|
||||
<p>On the server-side, I found myself using languages such as <em>PHP, Perl, and Python.</em> </p>
|
||||
<p>If you don't know any of the above, I can serve as your counsel, steering you toward the optimal solution for your project's specific goals.</p>
|
||||
<p>Should you have a standing website, yearning for expansion, toss me the code, and we'll unravel the possibilities together.</p>
|
||||
<p>Sometimes, the most fitting framework is none at all. I crafted some elegant, straightforward websites in the raw embrace of HTML, CSS, and JS.</p>
|
||||
</Faq>
|
||||
|
||||
<Faq summary="Which Content Management System (CMS) do you recommend?">
|
||||
<p>I've tinkered with various Content Management Systems — the likes of WordPress, Strapi, Builder, Magento, and Shopify. Each carries its own set of virtues and vices.</p>
|
||||
<Faq summary="Which <em>Content Management System</em> (CMS) do you recommend?">
|
||||
<p>I've worked with various Content Management Systems — the likes of WordPress, Strapi, Builder, Magento, and Shopify. Each carries its own set of virtues and vices.</p>
|
||||
<p>Let's talk about the folks responsible for constructing and upkeeping your content. Only then can we jointly discern the optimal solution.</p>
|
||||
</Faq>
|
||||
<Faq summary="Are your websites <em>SEO-optimised?</em>">
|
||||
<p>Search Engine Optimisation (SEO) is a collaborative effort between the developers of a website and the content creators. I can definitely provide the technical baseline for all content and metadata (eg page descriptions or manifests) to be accessible to search engines.</p>
|
||||
<p>However, the ranking of Search Results depends on many factors, and the biggest one is by far the quality of the content. If your content strategy is sound and attracts many users, resulting in other reputable pages linking to yours, that's when you'll surely climb the ladders on Google & Co's results.</p>
|
||||
</Faq>
|
||||
<Faq summary="Are your websites compliant with <em>Data Protection & Privacy Laws</em> (a.k.a. 'Cookie laws')?">
|
||||
<p>There are loads of Data Protection and Privacy Legislation laws in place today in many different countries.</p>
|
||||
<p>I can implement the necessary technical steps to become compliant, but I will rely on you to know which law is applicable to you and what that means in terms of requirements.</p>
|
||||
<p>These will also greatly depend on your own requirements, for example whether you will need to use analytics or other 3rd party software that collects user data.</p>
|
||||
<p>In many cases, the installation of a commercial software is the easiest way to reach compliance. These can auto-detect cookies and data-collection from 3rd parties, block them and display a consent-banner before reactivating them.</p>
|
||||
</Faq>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -122,6 +131,8 @@
|
|||
<li><em>Prototyping</em> for interactive products, to gain understanding of the user experience prior to production</li>
|
||||
<li><em>Illustration & animation</em> to bring your ideas to life</li>
|
||||
<li><em>Data visualisation</em> to aid crucial information cross the seas of abstraction</li>
|
||||
<a class="button" href="/work">Work examples</a>
|
||||
<a class="button button--primary" href="/contact">Get in touch!</a>
|
||||
</ul>
|
||||
<div class="faqs">
|
||||
<h3>FAQs</h3>
|
||||
|
|
@ -181,7 +192,7 @@
|
|||
|
||||
@media screen and (min-width: 768px) {
|
||||
font-size: 6vw;
|
||||
letter-spacing: -0.045em;
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
}
|
||||
.services {
|
||||
|
|
|
|||
|
|
@ -43,9 +43,10 @@
|
|||
let text = new PIXI.Text(string,
|
||||
{ fontFamily: 'Stratos',
|
||||
fontSize: fontSize,
|
||||
// fontWeight: '800',
|
||||
fontWeight: '800',
|
||||
fontStyle: 'italic',
|
||||
lineHeight: 0,
|
||||
letterSpacing: -20,
|
||||
letterSpacing: -10,
|
||||
fill: highLightColor,
|
||||
padding: 0
|
||||
}
|
||||
|
|
@ -131,7 +132,7 @@
|
|||
overwrite: true
|
||||
})
|
||||
mouse.y = e.clientY / window.innerHeight;
|
||||
})
|
||||
}, { passive: true })
|
||||
|
||||
let elapsed = 0;
|
||||
app.ticker.add((delta) => {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,117 @@
|
|||
<h1>
|
||||
Yee-ha!
|
||||
</h1>
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import * as PIXI from "pixi.js";
|
||||
|
||||
let app: PIXI.Application;
|
||||
let canvas: HTMLCanvasElement;
|
||||
|
||||
onMount(() => {
|
||||
app = new PIXI.Application({
|
||||
view: canvas,
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
backgroundColor: 0x000000,
|
||||
backgroundAlpha: 0,
|
||||
resizeTo: window
|
||||
});
|
||||
PIXI.Filter.defaultResolution = window.devicePixelRatio;
|
||||
|
||||
const container = new PIXI.Container();
|
||||
app.stage.addChild(container);
|
||||
|
||||
const envelope = PIXI.Sprite.from("/success/envelope.png");
|
||||
envelope.anchor.set(0.5);
|
||||
envelope.scale.set(0.15);
|
||||
envelope.x = app.screen.width / 2;
|
||||
envelope.y = envelope.height / 2 + 130;
|
||||
|
||||
container.addChild(envelope);
|
||||
|
||||
const text = new PIXI.Text("Thank you!", {
|
||||
fontFamily: "stratos",
|
||||
fontSize: 66,
|
||||
letterSpacing: -3,
|
||||
fill: 0xffead9,
|
||||
align: "center",
|
||||
});
|
||||
text.anchor.set(0.5);
|
||||
text.x = app.screen.width / 2;
|
||||
text.y = envelope.y + envelope.height / 2 + 90;
|
||||
container.addChild(text);
|
||||
text.resolution = window.devicePixelRatio;
|
||||
|
||||
const displacementSprite = PIXI.Sprite.from('/success/displacementMap.jpg');
|
||||
displacementSprite.texture.baseTexture.wrapMode = PIXI.WRAP_MODES.REPEAT;
|
||||
const displacementFilter = new PIXI.DisplacementFilter(displacementSprite);
|
||||
displacementFilter.scale.x = 0;
|
||||
displacementFilter.scale.y = 100;
|
||||
displacementFilter.padding = 50;
|
||||
|
||||
displacementSprite.position.x = envelope.position._x;
|
||||
|
||||
container.addChild(displacementSprite);
|
||||
|
||||
container.filters = [displacementFilter];
|
||||
|
||||
app.ticker.add(() =>
|
||||
{
|
||||
// Offset the sprite position to make vFilterCoord update to larger value.
|
||||
// Repeat wrapping makes sure there's still pixels on the coordinates.
|
||||
displacementSprite.x = displacementSprite.x + 6;
|
||||
// Reset x to 0 when it's over width to keep values from going to very huge numbers.
|
||||
if (displacementSprite.x > displacementSprite.width) { displacementSprite.x = 0; }
|
||||
});
|
||||
|
||||
return () => {
|
||||
app.destroy(true, true);
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<h1>Your message is underway.</h1>
|
||||
<p>I will get back to you as soon as I can and look forward to discussing further whatever you have sent me.</p>
|
||||
</div>
|
||||
<canvas bind:this={canvas}></canvas>
|
||||
|
||||
<style>
|
||||
canvas {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
padding: var(--spacing-outer) var(--spacing-outer) 60px var(--spacing-outer);
|
||||
margin: 0 auto;
|
||||
@media screen and (min-width: 768px) {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
line-height: .9;
|
||||
margin: 2em 0 0 0;
|
||||
color: var(--color-highlight);
|
||||
@media screen and (min-width: 768px) {
|
||||
font-size: 3.9em;
|
||||
margin: 1em 0 0 0;
|
||||
}
|
||||
}
|
||||
p {
|
||||
font-size: 1em;
|
||||
@media screen and (min-width: 768px) {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,53 +1,206 @@
|
|||
<script lang="ts">
|
||||
import WorkCanvas from '$lib/components/WorkCanvas.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { workClickHandler, initWorkPage } from './workUtils.js';
|
||||
import { workbulge } from '$lib/utils/stores.js';
|
||||
import { CldImage } from 'svelte-cloudinary';
|
||||
import gsap from 'gsap';
|
||||
import SplitText from 'gsap/dist/SplitText';
|
||||
export let data;
|
||||
|
||||
const orderedPosts = data.posts.sort((a, b) => {
|
||||
return a.meta.order - b.meta.order;
|
||||
});
|
||||
|
||||
let canvasTextElems: Array<HTMLElement>;
|
||||
let canvasImgElems: Array<HTMLElement>;
|
||||
let bulge = {factor: 0};
|
||||
|
||||
workbulge.subscribe((val) => {
|
||||
bulge.factor = val;
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
const headline: HTMLElement = document.querySelector('.headline') as HTMLElement;
|
||||
const headlines: Array<HTMLElement> = Array.from(document.querySelectorAll('h2, li'));
|
||||
const images: Array<HTMLElement> = Array.from(document.querySelectorAll('.work img'));
|
||||
let canvasElems = initWorkPage( headlines, images );
|
||||
canvasTextElems = canvasElems.text as Array<HTMLElement>;
|
||||
canvasImgElems = canvasElems.images;
|
||||
|
||||
gsap.registerPlugin(SplitText);
|
||||
|
||||
let isZoomed = false;
|
||||
|
||||
let works: Array<HTMLElement> = Array.from(document.querySelectorAll('.work'));
|
||||
for ( let i = 0; i < 40; i++ ){
|
||||
let clone = works[i].cloneNode(true) as HTMLElement;
|
||||
works[i].parentNode?.appendChild(clone);
|
||||
works.push(clone);
|
||||
}
|
||||
|
||||
works.forEach((work, index) => {
|
||||
let workrect = work.getBoundingClientRect();
|
||||
let worksrect = document.querySelector('.works')?.getBoundingClientRect() || {x: 0, y: 0, width: 1, height: 1};
|
||||
let distanceXinPercent = (workrect.x - worksrect?.x || 0) / (worksrect?.width || 1) * 100 ;
|
||||
let distanceYinPercent = (workrect.y - worksrect?.y || 0) / (worksrect?.height || 1) * 100 ;
|
||||
|
||||
work.addEventListener('click', (e) => {
|
||||
isZoomed = !isZoomed;
|
||||
e.preventDefault();
|
||||
let href = work.getAttribute('href');
|
||||
if (isZoomed) {
|
||||
gsap.to('.works', {
|
||||
rotate: 0,
|
||||
xPercent: -distanceXinPercent,
|
||||
yPercent: -distanceYinPercent,
|
||||
rotateX: 0,
|
||||
rotateY: 0,
|
||||
scale: 1,
|
||||
duration: .75,
|
||||
ease: 'power2.inOut',
|
||||
onComplete: () => {
|
||||
let workclone = work.cloneNode(true) as HTMLElement;
|
||||
workclone.classList.add('workclone');
|
||||
workclone.style.position = 'fixed';
|
||||
workclone.style.top = `0`;
|
||||
workclone.style.left = `0`;
|
||||
document.body.appendChild(workclone);
|
||||
window.location.href = href || ''
|
||||
}
|
||||
})
|
||||
gsap.fromTo(split.chars, {
|
||||
y: 0,
|
||||
opacity: 1
|
||||
}, {
|
||||
y: -window.innerHeight/2,
|
||||
opacity: 0,
|
||||
duration: .6,
|
||||
stagger: -0.0075,
|
||||
ease: 'power3.out',
|
||||
})
|
||||
} else {
|
||||
gsap.to('.works', {
|
||||
rotate: -5,
|
||||
transformOrigin: '0 0',
|
||||
xPercent: -3,
|
||||
yPercent: 0,
|
||||
rotateX: 0,
|
||||
rotateY: 0,
|
||||
scale: .25,
|
||||
duration: 1,
|
||||
ease: 'power4.inOut'
|
||||
})
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// set initial positions AFTER the individual rects have been read
|
||||
|
||||
let split = new SplitText('.headline', { type: 'chars', charsClass: 'charChildren' });
|
||||
|
||||
gsap.set('.works, .headline', {
|
||||
rotate: -5,
|
||||
xPercent: -5,
|
||||
yPercent: -2,
|
||||
rotateX: 0,
|
||||
rotateY: 0,
|
||||
scale: .25,
|
||||
})
|
||||
gsap.set('.work, .works, .headline', {
|
||||
transformOrigin: '0 0',
|
||||
})
|
||||
gsap.fromTo('.work',{
|
||||
opacity: 1,
|
||||
yPercent: 50,
|
||||
scaleY: 0,
|
||||
},{
|
||||
opacity:1,
|
||||
yPercent: 0,
|
||||
scaleY: 1,
|
||||
duration: .75,
|
||||
stagger: 0.01,
|
||||
ease: 'power4.inOut',
|
||||
})
|
||||
gsap.fromTo(split.chars, {
|
||||
yPercent: -300
|
||||
}, {
|
||||
yPercent: 0,
|
||||
duration: 1,
|
||||
stagger: 0.0075,
|
||||
ease: 'elastic.out(1, .8)',
|
||||
})
|
||||
// Move the works around on mousemove
|
||||
let mouseMoveHandler = (e: MouseEvent) => {
|
||||
let x = e.clientX;
|
||||
let y = e.clientY;
|
||||
if (isZoomed) return;
|
||||
gsap.to('.works, .headline', {
|
||||
xPercent: -x/window.innerWidth * 4 - 3,
|
||||
yPercent: -y/window.innerHeight * 4,
|
||||
rotateX: x/window.innerWidth * 10 - 5,
|
||||
rotateY: x/window.innerWidth * 10 - 5,
|
||||
duration: 1,
|
||||
ease: 'power4.out',
|
||||
overwrite: true,
|
||||
})
|
||||
}
|
||||
// Hover states or .work
|
||||
works.forEach((work, index) => {
|
||||
work.addEventListener('mouseenter', (e) => {
|
||||
if (isZoomed) return;
|
||||
gsap.to(work, {
|
||||
backgroundColor: 'var(--color-bg)',
|
||||
duration: .5,
|
||||
ease: 'power1.inOut',
|
||||
})
|
||||
gsap.to(work.querySelector('.work-logo'), {
|
||||
color: 'var(--color-highlight)',
|
||||
duration: .5,
|
||||
ease: 'power1.inOut',
|
||||
})
|
||||
})
|
||||
work.addEventListener('mouseleave', (e) => {
|
||||
if (isZoomed) return;
|
||||
gsap.to(work, {
|
||||
backgroundColor: 'var(--color-highlight)',
|
||||
duration: .5,
|
||||
ease: 'power1.inOut',
|
||||
})
|
||||
gsap.to(work.querySelector('.work-logo'), {
|
||||
color: 'var(--color-bg)',
|
||||
duration: .5,
|
||||
ease: 'power1.inOut',
|
||||
})
|
||||
})
|
||||
})
|
||||
window.addEventListener('mousemove', mouseMoveHandler);
|
||||
|
||||
// Move the works the same way on touch drag
|
||||
let touchMoveHandler = (e: TouchEvent) => {
|
||||
let x = e.touches[0].clientX;
|
||||
let y = e.touches[0].clientY;
|
||||
if (isZoomed) return;
|
||||
gsap.to('.works, .headline', {
|
||||
xPercent: -x/window.innerWidth * 4 - 3.33,
|
||||
yPercent: -y/window.innerHeight * 4,
|
||||
rotateX: x/window.innerWidth * 10 - 5,
|
||||
rotateY: x/window.innerWidth * 10 - 5,
|
||||
duration: 1,
|
||||
ease: 'power4.out',
|
||||
overwrite: true,
|
||||
})
|
||||
}
|
||||
window.addEventListener('touchmove', touchMoveHandler, { passive: true });
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('mousemove', mouseMoveHandler);
|
||||
window.removeEventListener('touchmove', touchMoveHandler);
|
||||
gsap.killTweensOf('.works, .work');
|
||||
works.forEach((work, index) => {
|
||||
work.removeEventListener('mouseenter', (e) => {});
|
||||
work.removeEventListener('mouseleave', (e) => {});
|
||||
work.removeEventListener('click', (e) => {});
|
||||
})
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<div class="works-wrapper">
|
||||
|
||||
<h1 class="headline"><span>Outstanding work</span></h1>
|
||||
<div class="works-wrapper">
|
||||
<h1 class="headline"><span>Work References</span></h1>
|
||||
<div class="works">
|
||||
|
||||
{#each orderedPosts as work, i}
|
||||
<a
|
||||
data-sveltekit-preload-data
|
||||
href="{work.path}"
|
||||
class="work"
|
||||
on:click={ (e) => workClickHandler(e) }
|
||||
>
|
||||
<CldImage
|
||||
src={work.meta.header_bg_image}
|
||||
sizes={ i === 0 ? `(min-width: 768px) 60vw, 50vw` : `(min-width: 768px) 20vw, 50vw`}
|
||||
alt={work.meta.title}
|
||||
width="2100"
|
||||
height="1400"
|
||||
objectFit="fill"
|
||||
loading= "lazy"
|
||||
/>
|
||||
<div class="work-logo">
|
||||
{@html work.meta.svg}
|
||||
</div>
|
||||
<div class="work-info">
|
||||
<h2 class="title"><span class="title-words">{work.meta.title}</span></h2>
|
||||
{#if work.meta.tags}
|
||||
|
|
@ -61,11 +214,6 @@
|
|||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
<WorkCanvas
|
||||
textsToCanvas={canvasTextElems}
|
||||
imgsToCanvas={canvasImgElems}
|
||||
bulgeFactor={bulge.factor}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style src="./work.scss" lang="scss"></style>
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
export async function load( { params }: { params: { slug: string }} ){
|
||||
try {
|
||||
const post = await import(`../md/${params.slug}.md`)
|
||||
const { title, date, header_bg_image, svg, video, tags } = post.metadata
|
||||
const { title = '', date = '', header_bg_image = '', svg = '', video = '', tags = [], reference = '', referenceName = '', tasks = [], description = [], images = [], agency = '', agencyName = '' } = post.metadata
|
||||
|
||||
const Content = post.default.render()
|
||||
|
||||
return {
|
||||
|
|
@ -12,6 +13,13 @@ export async function load( { params }: { params: { slug: string }} ){
|
|||
video,
|
||||
tags,
|
||||
Content,
|
||||
reference,
|
||||
referenceName,
|
||||
tasks,
|
||||
description,
|
||||
images,
|
||||
agency,
|
||||
agencyName,
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
|
|
|||
|
|
@ -2,278 +2,319 @@
|
|||
import { onMount, onDestroy } from 'svelte';
|
||||
import { gsap } from 'gsap';
|
||||
import { ScrollTrigger } from 'gsap/dist/ScrollTrigger';
|
||||
import { CldImage } from 'svelte-cloudinary';
|
||||
import ScrollToPlugin from 'gsap/dist/ScrollToPlugin';
|
||||
import { CldImage, CldVideoPlayer } from 'svelte-cloudinary';
|
||||
export let data;
|
||||
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
let visible = false;
|
||||
|
||||
function animForDesktop() {
|
||||
|
||||
ScrollTrigger.getAll().forEach(t => t.kill());
|
||||
|
||||
gsap.to('.heromask, .coverclone', { duration: .6, x: "-10%", ease: "cubic.inOut" })
|
||||
|
||||
gsap.to('.work', {
|
||||
xPercent: -100,
|
||||
duration: .6,
|
||||
ease: "expo.out",
|
||||
delay: .2
|
||||
})
|
||||
|
||||
let heroheight = document.querySelector('.heromask')?.getBoundingClientRect().height || 100;
|
||||
|
||||
gsap.to('.heromask', {
|
||||
clipPath: "polygon(0 0, 60% 0, 35% 100%, 0% 100%)",
|
||||
duration: 1,
|
||||
ease: "power4.out",
|
||||
onStart: () => {
|
||||
setTimeout(() => {
|
||||
document.querySelector('.coverclone')?.remove();
|
||||
}, 100);
|
||||
},
|
||||
onComplete: () => {
|
||||
gsap.to('.heromask', {
|
||||
ease: "none",
|
||||
clipPath: "polygon(0 0, 50% 0, 50% 100%, 0% 100%)",
|
||||
scrollTrigger: {
|
||||
trigger: '.work',
|
||||
start: 'top top',
|
||||
end: `200px top`,
|
||||
scrub: true
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
function animForMobile() {
|
||||
ScrollTrigger.getAll().forEach(t => t.kill());
|
||||
gsap.to('.heromask, .coverclone', { duration: .6, y: -20, ease: "cubic.inOut" })
|
||||
|
||||
gsap.to('.work', {
|
||||
opacity: 1,
|
||||
yPercent: -100,
|
||||
duration: .4,
|
||||
ease: "expo.out",
|
||||
delay: .2,
|
||||
})
|
||||
|
||||
gsap.to('.heromask', {
|
||||
clipPath: "polygon(0 0, 100% 0, 100% 75%, 0% 100%)",
|
||||
duration: .6,
|
||||
ease: "cubic.inOut",
|
||||
onStart: () => {
|
||||
setTimeout(() => {
|
||||
document.querySelector('.coverclone')?.remove();
|
||||
}, 100);
|
||||
},
|
||||
onComplete: () => {
|
||||
gsap.to('.heromask', {
|
||||
ease: "power1.out",
|
||||
scrollTrigger: {
|
||||
trigger: '.work',
|
||||
markers: false,
|
||||
start: '0px -10px',
|
||||
end: `0px -20px`,
|
||||
scrub: false,
|
||||
onEnterBack: () => {
|
||||
gsap.to('.heromask', {
|
||||
clipPath: "polygon(0 0, 100% 0, 100% 75%, 0% 100%)",
|
||||
duration: .6,
|
||||
ease: "expo.out",
|
||||
})
|
||||
},
|
||||
onEnter: () => {
|
||||
gsap.to('.heromask', {
|
||||
clipPath: "polygon(0 0, 100% 0, 100% 0%, 0% 0%)",
|
||||
duration: .6,
|
||||
ease: "expo.out",
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
function animForSize(){
|
||||
if ( window.matchMedia("(min-width: 768px) and (orientation: landscape)").matches ) {
|
||||
animForDesktop();
|
||||
} else {
|
||||
animForMobile();
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
||||
visible = true;
|
||||
|
||||
document.querySelector('.heromask img')?.addEventListener('load', () => {
|
||||
animForSize();
|
||||
|
||||
let portrait = window.matchMedia("(orientation: portrait)");
|
||||
|
||||
portrait.addEventListener("change", function(e) {
|
||||
animForSize();
|
||||
})
|
||||
if (document.querySelector('.workclone')) {
|
||||
document.querySelector('.workclone')?.remove();
|
||||
}
|
||||
gsap.to('.logo-wrapper', {
|
||||
opacity: 0,
|
||||
zIndex: -1,
|
||||
duration: 1,
|
||||
ease: 'power2.out',
|
||||
})
|
||||
gsap.from('.work', {
|
||||
y: '100vh',
|
||||
duration: 1,
|
||||
ease: 'power4.out',
|
||||
})
|
||||
gsap.from('.gallery-wrapper', {
|
||||
y: '-100vh',
|
||||
duration: 1,
|
||||
ease: 'power4.out',
|
||||
})
|
||||
|
||||
const gallery = document.querySelector('.gallery') as HTMLElement;
|
||||
let startX: number;
|
||||
let scrollLeft: number;
|
||||
let isDown = false;
|
||||
if (gallery) {
|
||||
gallery.addEventListener('mousedown', (e) => {
|
||||
isDown = true;
|
||||
startX = e.pageX - gallery.offsetLeft;
|
||||
scrollLeft = gallery.scrollLeft;
|
||||
});
|
||||
gallery.addEventListener('mouseleave', () => {
|
||||
isDown = false;
|
||||
});
|
||||
gallery.addEventListener('mouseup', () => {
|
||||
isDown = false;
|
||||
});
|
||||
gallery.addEventListener('mousemove', (e) => {
|
||||
if (!isDown) return;
|
||||
e.preventDefault();
|
||||
const x = e.pageX - gallery.offsetLeft;
|
||||
const walk = (x - startX) * 4; //scroll-fast
|
||||
gallery.scrollLeft = scrollLeft - walk;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return () => {
|
||||
gsap.killTweensOf('.logo-wrapper, .work, .gallery-wrapper');
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<div class="heromask">
|
||||
<CldImage
|
||||
src={data.header_bg_image}
|
||||
alt="{data.title}"
|
||||
sizes="100vw"
|
||||
width={2100}
|
||||
height={1400}
|
||||
placeholder="blur"
|
||||
loading="eager"
|
||||
objectFit="fill"
|
||||
/>
|
||||
<div class="logo-wrapper">
|
||||
<div class="svg-logo">{@html data.svg}</div>
|
||||
</div>
|
||||
<div class="subnav">
|
||||
<a href="/work" class="subnav-item">← Back</a>
|
||||
<a href="/work" class="subnav-item button">← Back</a>
|
||||
</div>
|
||||
<div class="gallery-wrapper">
|
||||
<div class="gallery">
|
||||
{#if visible}
|
||||
{#each data.images as image}
|
||||
<figure>
|
||||
{#if image.includes('/video/')}
|
||||
<video src={image} width="1400" height="840" autoplay muted loop></video>
|
||||
{:else}
|
||||
<CldImage
|
||||
src={image}
|
||||
alt="{data.title}"
|
||||
sizes="(min-width: 768px) 67vw, 90vw"
|
||||
width={1400}
|
||||
height={840}
|
||||
placeholder="blur"
|
||||
loading="eager"
|
||||
objectFit="cover"
|
||||
/>
|
||||
{/if}
|
||||
</figure>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<article class="work">
|
||||
{#if visible}
|
||||
{#if visible}
|
||||
<h1>{data.title}</h1>
|
||||
<div class="description">
|
||||
{data.description}
|
||||
</div>
|
||||
<div class="work-content">
|
||||
{#if data.tags != undefined && data.tags.length > 0 }
|
||||
<div class="tags">
|
||||
{#each data.tags as tag }
|
||||
<div class="tag">{tag}</div>
|
||||
{ /each }
|
||||
<div class="infobox">
|
||||
<div class="tasks">
|
||||
<div class="tasks-title">What I did:</div>
|
||||
<ul>
|
||||
{#each data.tags as tag }
|
||||
<li>{tag}</li>
|
||||
{ /each }
|
||||
</ul>
|
||||
</div>
|
||||
{/if}
|
||||
<h1><span class="svg-logo">{@html data.svg}</span><span class="name">{data.title}</span></h1>
|
||||
<div class="reference">
|
||||
<div class="reference-title">Reference:</div>
|
||||
<div><a href={data.reference}>{data.referenceName}</a></div>
|
||||
</div>
|
||||
{#if data.agency}
|
||||
<div class="agency">
|
||||
<div class="agency-title">Agency:</div>
|
||||
<div><a href={data.agency}>{data.agencyName}</a></div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="work-content-text">
|
||||
{@html data.Content.html}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</article>
|
||||
|
||||
<style lang="scss">
|
||||
.work {
|
||||
width: 100vw;
|
||||
min-height: 100svh;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
transform: translateY(100%);
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
.subnav {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 4;
|
||||
padding: var(--spacing-outer);
|
||||
|
||||
& a {
|
||||
text-decoration: none;
|
||||
color: var(--color-highlight);
|
||||
}
|
||||
}
|
||||
.heromask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left:0;
|
||||
aspect-ratio: var(--aspect-ratio-heroes);
|
||||
width: 100%;
|
||||
height: auto;
|
||||
z-index: 2;
|
||||
clip-path: polygon(0 0, 100% 0, 100% 100%, 0% 100%);
|
||||
}
|
||||
:global(.heromask img) {
|
||||
z-index: 0;
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
aspect-ratio: var(--aspect-ratio-heroes);
|
||||
margin: 0;
|
||||
object-fit: fill;
|
||||
}
|
||||
.work-content {
|
||||
padding: 0 var(--spacing-outer);
|
||||
padding-top: calc(66.6vw + 1em);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
color: var(--color-text);
|
||||
|
||||
& > :last-child {
|
||||
margin-bottom: 100px;
|
||||
}
|
||||
@media screen and (min-width: 768px) {
|
||||
margin-left: 40vw;
|
||||
max-width: 60vw;
|
||||
padding-top: calc( 3 * var(--spacing-outer) );
|
||||
padding-left: calc(var(--spacing-outer) * 1.5);
|
||||
padding-right: calc(var(--spacing-outer) * 2.5);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: inline-block;
|
||||
z-index: 22;
|
||||
}
|
||||
}
|
||||
.subnav-item {
|
||||
margin: var(--spacing-nav);
|
||||
}
|
||||
.logo-wrapper {
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 1;
|
||||
}
|
||||
.svg-logo {
|
||||
color: var(--color-highlight);
|
||||
padding: 3em;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
@media screen and (min-width: 768px) {
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
:global(.svg-logo svg) {
|
||||
object-fit: fill;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.work {
|
||||
overflow: hidden;
|
||||
padding: 0 var(--spacing-outer) 100px var(--spacing-outer);
|
||||
@media screen and (min-width: 768px) {
|
||||
margin: 0 var(--spacing-outer) 0 150px;
|
||||
padding: 0 var(--spacing-outer) 100px var(--spacing-outer);
|
||||
width: calc(100% - 150px - var(--spacing-outer));
|
||||
}
|
||||
}
|
||||
h1 {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin: 0;
|
||||
|
||||
margin: .5em 0 0.25em 0;
|
||||
font-size: 2.5rem;
|
||||
@media screen and (min-width: 768px) {
|
||||
padding: 0 0 1em 0;
|
||||
font-size: 5rem;
|
||||
}
|
||||
& .name {
|
||||
}
|
||||
.description {
|
||||
margin-bottom: 1.5em;
|
||||
line-height: 1.3;
|
||||
letter-spacing: -0.0075em;
|
||||
font-size: 1.25rem;
|
||||
@media screen and (min-width: 768px) {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
.infobox {
|
||||
font-size: 1.25rem;
|
||||
padding: 0 0 2em 0;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
flex: 0 0 25%;
|
||||
}
|
||||
}
|
||||
.reference-title, .agency-title, .tasks-title {
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
margin: 0 0 .125em 0;
|
||||
}
|
||||
.reference-title, .agency-title {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.tasks ul {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: .125em;
|
||||
}
|
||||
.tasks li {
|
||||
font-size: 1rem;
|
||||
list-style: none;
|
||||
border: none;
|
||||
text-transform: uppercase;
|
||||
border-radius: 3px;
|
||||
margin: 0;
|
||||
padding: 0.25em 0.33em;
|
||||
background-color: var(--color-highlight);
|
||||
color: var(--color-bg);
|
||||
}
|
||||
.work-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
@media screen and (min-width: 768px) {
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
}
|
||||
.work-content-text :global(h2) {
|
||||
font-size: 2rem;
|
||||
@media screen and (min-width: 768px) {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
.work-content-text > :global(:first-child) {
|
||||
margin-top: 0;
|
||||
}
|
||||
.work-content-text :global(p) {
|
||||
font-size: 1rem;
|
||||
@media screen and (min-width: 768px) {
|
||||
font-size: 1em;
|
||||
}
|
||||
}
|
||||
.work-content-text > :global(h1):first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.gallery-wrapper {
|
||||
position: relative;
|
||||
width: 100vw;
|
||||
overflow: hidden;
|
||||
margin-bottom: -80px;
|
||||
top: -80px;
|
||||
padding: 0 0 1em 0;
|
||||
z-index: 1;
|
||||
opacity: 0.75;
|
||||
perspective: 250px;
|
||||
perspective-origin: center bottom;
|
||||
@media screen and (min-width: 768px) {
|
||||
padding: 2em 0 4em 0;
|
||||
margin-bottom: -120px;
|
||||
perspective: 500px;
|
||||
}
|
||||
}
|
||||
.gallery {
|
||||
transform-origin: 50% 50%;
|
||||
position: relative;
|
||||
display: flex;
|
||||
scroll-behavior: smooth;
|
||||
gap: 1px;
|
||||
overflow-x: scroll;
|
||||
scroll-snap-type: x mandatory;
|
||||
width: 115vw;
|
||||
left: -4vw;
|
||||
cursor: grab;
|
||||
transform: rotate3d(-2, 0, 1, -10deg);
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
& .svg-logo :global(svg) {
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-width: 250px;
|
||||
max-height: 80px;
|
||||
margin-bottom: 1em;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
max-width: 400px;
|
||||
max-height: 200px;
|
||||
}
|
||||
@media screen and (min-width: 768px) {
|
||||
width: 110vw;
|
||||
left: -3vw;
|
||||
transform: rotate3d(-2, 0, 1, -10deg);
|
||||
}
|
||||
}
|
||||
.tags {
|
||||
padding-bottom: .25em;
|
||||
font-size: 1em;
|
||||
margin-bottom: 2em;
|
||||
line-height: 1.1;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: calc(100% + var(--spacing-outer) * 2.5);
|
||||
height: 1px;
|
||||
background-color: var(--color-text);
|
||||
margin-top: .5em;
|
||||
figure {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
justify-content: center;
|
||||
scroll-snap-align: start;
|
||||
flex: 1 0 85%;
|
||||
background-color: var(--color-highlight);
|
||||
height: 100%;
|
||||
aspect-ratio: 140/84;
|
||||
width: auto;
|
||||
margin: 0;
|
||||
@media screen and (min-width: 768px) {
|
||||
flex: 1 0 66.666%;
|
||||
}
|
||||
}
|
||||
.tag {
|
||||
display: inline-block;
|
||||
margin-right: .5em;
|
||||
padding: .125em 0;
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: -.005em;
|
||||
|
||||
&:after {
|
||||
content: ','
|
||||
|
||||
& :global(img), video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-position: left top;
|
||||
}
|
||||
&:last-child:after {
|
||||
content: none
|
||||
video {
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.header-nav){
|
||||
transition: all .3s cubic-bezier(0.075, 0.82, 0.165, 1);
|
||||
}
|
||||
:global(.work .header-nav){
|
||||
transform: translateY(100%);
|
||||
}
|
||||
</style>
|
||||
279
src/routes/work/[slug]/old+page.svelte
Normal file
279
src/routes/work/[slug]/old+page.svelte
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
<script lang="ts">
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import { gsap } from 'gsap';
|
||||
import { ScrollTrigger } from 'gsap/dist/ScrollTrigger';
|
||||
import { CldImage } from 'svelte-cloudinary';
|
||||
export let data;
|
||||
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
let visible = false;
|
||||
|
||||
function animForDesktop() {
|
||||
|
||||
ScrollTrigger.getAll().forEach(t => t.kill());
|
||||
|
||||
gsap.to('.heromask, .coverclone', { duration: .6, x: "-10%", ease: "cubic.inOut" })
|
||||
|
||||
gsap.to('.work', {
|
||||
xPercent: -100,
|
||||
duration: .6,
|
||||
ease: "expo.out",
|
||||
delay: .2
|
||||
})
|
||||
|
||||
let heroheight = document.querySelector('.heromask')?.getBoundingClientRect().height || 100;
|
||||
|
||||
gsap.to('.heromask', {
|
||||
clipPath: "polygon(0 0, 60% 0, 35% 100%, 0% 100%)",
|
||||
duration: 1,
|
||||
ease: "power4.out",
|
||||
onStart: () => {
|
||||
setTimeout(() => {
|
||||
document.querySelector('.coverclone')?.remove();
|
||||
}, 100);
|
||||
},
|
||||
onComplete: () => {
|
||||
gsap.to('.heromask', {
|
||||
ease: "none",
|
||||
clipPath: "polygon(0 0, 50% 0, 50% 100%, 0% 100%)",
|
||||
scrollTrigger: {
|
||||
trigger: '.work',
|
||||
start: 'top top',
|
||||
end: `200px top`,
|
||||
scrub: true
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
function animForMobile() {
|
||||
ScrollTrigger.getAll().forEach(t => t.kill());
|
||||
gsap.to('.heromask, .coverclone', { duration: .6, y: -20, ease: "cubic.inOut" })
|
||||
|
||||
gsap.to('.work', {
|
||||
opacity: 1,
|
||||
yPercent: -100,
|
||||
duration: .4,
|
||||
ease: "expo.out",
|
||||
delay: .2,
|
||||
})
|
||||
|
||||
gsap.to('.heromask', {
|
||||
clipPath: "polygon(0 0, 100% 0, 100% 75%, 0% 100%)",
|
||||
duration: .6,
|
||||
ease: "cubic.inOut",
|
||||
onStart: () => {
|
||||
setTimeout(() => {
|
||||
document.querySelector('.coverclone')?.remove();
|
||||
}, 100);
|
||||
},
|
||||
onComplete: () => {
|
||||
gsap.to('.heromask', {
|
||||
ease: "power1.out",
|
||||
scrollTrigger: {
|
||||
trigger: '.work',
|
||||
markers: false,
|
||||
start: '0px -10px',
|
||||
end: `0px -20px`,
|
||||
scrub: false,
|
||||
onEnterBack: () => {
|
||||
gsap.to('.heromask', {
|
||||
clipPath: "polygon(0 0, 100% 0, 100% 75%, 0% 100%)",
|
||||
duration: .6,
|
||||
ease: "expo.out",
|
||||
})
|
||||
},
|
||||
onEnter: () => {
|
||||
gsap.to('.heromask', {
|
||||
clipPath: "polygon(0 0, 100% 0, 100% 0%, 0% 0%)",
|
||||
duration: .6,
|
||||
ease: "expo.out",
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
function animForSize(){
|
||||
if ( window.matchMedia("(min-width: 768px) and (orientation: landscape)").matches ) {
|
||||
animForDesktop();
|
||||
} else {
|
||||
animForMobile();
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
||||
visible = true;
|
||||
|
||||
document.querySelector('.heromask img')?.addEventListener('load', () => {
|
||||
animForSize();
|
||||
|
||||
let portrait = window.matchMedia("(orientation: portrait)");
|
||||
|
||||
portrait.addEventListener("change", function(e) {
|
||||
animForSize();
|
||||
})
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="heromask">
|
||||
<CldImage
|
||||
src={data.header_bg_image}
|
||||
alt="{data.title}"
|
||||
sizes="100vw"
|
||||
width={2100}
|
||||
height={1400}
|
||||
placeholder="blur"
|
||||
loading="eager"
|
||||
objectFit="fill"
|
||||
/>
|
||||
</div>
|
||||
<div class="subnav">
|
||||
<a href="/work" class="subnav-item">← Back</a>
|
||||
</div>
|
||||
<article class="work">
|
||||
{#if visible}
|
||||
<div class="work-content">
|
||||
{#if data.tags != undefined && data.tags.length > 0 }
|
||||
<div class="tags">
|
||||
{#each data.tags as tag }
|
||||
<div class="tag">{tag}</div>
|
||||
{ /each }
|
||||
</div>
|
||||
{/if}
|
||||
<h1><span class="svg-logo">{@html data.svg}</span><span class="name">{data.title}</span></h1>
|
||||
<div class="work-content-text">
|
||||
{@html data.Content.html}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</article>
|
||||
|
||||
<style lang="scss">
|
||||
.work {
|
||||
width: 100vw;
|
||||
min-height: 100svh;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
transform: translateY(100%);
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
.subnav {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 4;
|
||||
padding: var(--spacing-outer);
|
||||
|
||||
& a {
|
||||
text-decoration: none;
|
||||
color: var(--color-highlight);
|
||||
}
|
||||
}
|
||||
.heromask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left:0;
|
||||
aspect-ratio: var(--aspect-ratio-heroes);
|
||||
width: 100%;
|
||||
height: auto;
|
||||
z-index: 2;
|
||||
clip-path: polygon(0 0, 100% 0, 100% 100%, 0% 100%);
|
||||
}
|
||||
:global(.heromask img) {
|
||||
z-index: 0;
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
aspect-ratio: var(--aspect-ratio-heroes);
|
||||
margin: 0;
|
||||
object-fit: fill;
|
||||
}
|
||||
.work-content {
|
||||
padding: 0 var(--spacing-outer);
|
||||
padding-top: calc(66.6vw + 1em);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
color: var(--color-text);
|
||||
|
||||
& > :last-child {
|
||||
margin-bottom: 100px;
|
||||
}
|
||||
@media screen and (min-width: 768px) {
|
||||
margin-left: 40vw;
|
||||
max-width: 60vw;
|
||||
padding-top: calc( 3 * var(--spacing-outer) );
|
||||
padding-left: calc(var(--spacing-outer) * 1.5);
|
||||
padding-right: calc(var(--spacing-outer) * 2.5);
|
||||
}
|
||||
}
|
||||
h1 {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin: 0;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
padding: 0 0 1em 0;
|
||||
}
|
||||
& .name {
|
||||
display: none;
|
||||
}
|
||||
& .svg-logo :global(svg) {
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-width: 250px;
|
||||
max-height: 80px;
|
||||
margin-bottom: 1em;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
max-width: 400px;
|
||||
max-height: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.tags {
|
||||
padding-bottom: .25em;
|
||||
font-size: 1em;
|
||||
margin-bottom: 2em;
|
||||
line-height: 1.1;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: calc(100% + var(--spacing-outer) * 2.5);
|
||||
height: 1px;
|
||||
background-color: var(--color-text);
|
||||
margin-top: .5em;
|
||||
}
|
||||
}
|
||||
.tag {
|
||||
display: inline-block;
|
||||
margin-right: .5em;
|
||||
padding: .125em 0;
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: -.005em;
|
||||
|
||||
&:after {
|
||||
content: ','
|
||||
}
|
||||
&:last-child:after {
|
||||
content: none
|
||||
}
|
||||
}
|
||||
|
||||
:global(.header-nav){
|
||||
transition: all .3s cubic-bezier(0.075, 0.82, 0.165, 1);
|
||||
}
|
||||
:global(.work .header-nav){
|
||||
transform: translateY(100%);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,13 +1,16 @@
|
|||
---
|
||||
title: Adidas
|
||||
header_bg_image: adidas_hero_ceurd8
|
||||
images: [ '/work/adidas/adidas_hero_ceurd8', '/work/adidas/adidas-mockup_ozihu3', '/work/adidas/adidas-illus-bg_t2t5ts' ]
|
||||
order: 12
|
||||
tags: ['graphic design', 'illustration']
|
||||
description: Adidas, as you might have heard, is an international sports and lifestyle clothing brand. I worked on several digital campaigns as a graphic designer and illustrator.
|
||||
description: Design & Illustration for the international sports and lifestyle clothing brand.
|
||||
svg: '<svg id="adidas-logo" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 1000 674.2" style="enable-background:new 0 0 1000 674.2; fill: currentColor" xml:space="preserve"><path d="M654.5 442.2 448.9 84.8 596.6 0l255.7 442.2H654.5"/><path d="m106.8 392.1 147.7-85.4 78.2 135.5H135.5l-28.7-50.1"/><path d="M396.7 670.2h42V500.1h-42v170.1z"/><path d="M923.6 674.2c-47 0-75.3-24.3-76.8-58.5h44.3c0 10.7 6.7 26.4 35.4 26.9 19.1 0 28.1-11.3 28.1-19.7-1.1-13.4-18-14.5-35.9-17.4-18-2.9-33.3-6.1-44.3-11.8-14.1-7.3-23.7-22.9-23.7-40.9 0-30.4 26.4-54.5 70.3-54.5 42.6 0 69.6 22.4 72.4 55.6h-42.8c-.4-9-2.1-23.1-27.3-23.1-17 0-28.3 3.4-29.2 15.3 0 17.4 35.4 16.2 62.9 23.5 26.4 6.7 43.2 23.1 43.2 46.1-.2 42.2-34.4 58.5-76.6 58.5"/><path d="m280 240.4 147.7-85.2 165.7 287H438.8v42h-42V442L280 240.4"/><path class="st0" d="M283.8 674.2c-48.9 0-88.7-39.9-88.7-88.3 0-48.9 39.7-87.5 88.7-87.5 18.5 0 35.4 5 50.1 15.1v-71.3h42v228h-42v-11.3c-14.8 9.6-31.6 15.3-50.1 15.3zm-48.4-88.3c0 26.4 22.5 48.3 49.5 48.3 26.4 0 48.9-22 48.9-48.3 0-26.4-22.5-48.9-48.9-48.9-26.9 0-49.5 22.5-49.5 48.9"/><path class="st0" d="M594.5 442.2H636v228h-41.5v-11.3c-14.1 9.6-31.5 15.3-50.6 15.3-48.3 0-88.1-39.9-88.1-88.3 0-48.9 39.7-87.5 88.1-87.5 19.1 0 35.9 5 50.6 15.1v-71.3zm-97.8 143.7c0 26.4 22.5 48.3 48.3 48.3 26.9 0 49.5-22 49.5-48.3 0-26.4-22.5-48.9-49.5-48.9-25.8 0-48.3 22.5-48.3 48.9"/><path class="st0" d="M738.2 674.2c-48.2 0-88.1-39.9-88.1-88.3 0-48.9 39.9-87.5 88.1-87.5 18.5 0 35.9 5 50.1 15.1v-13.6h42v170.3h-42v-11.3c-14.2 9.6-31 15.3-50.1 15.3zM691 585.9c0 26.4 22.5 48.3 48.9 48.3s48.3-22 48.3-48.3c0-26.4-22-48.9-48.3-48.9-26.4 0-48.9 22.5-48.9 48.9"/><path class="st0" d="M40.5 585.9c0 26.4 22.5 48.3 48.9 48.3 26.9 0 49.5-22 49.5-48.3 0-26.4-22.5-48.9-49.5-48.9-26.3 0-48.9 22.5-48.9 48.9zm47.8 88.3c-48.4 0-88.3-40-88.3-88.3 0-48.9 39.9-87.5 88.3-87.5 18.5 0 35.9 5 50.6 15.1v-13.6h41.5v170.3h-41.5v-11.3c-14.1 9.6-31.5 15.3-50.6 15.3"/></svg>'
|
||||
reference: https://www.linkedin.com/in/stanislasdupart/
|
||||
referenceName: Stan Dupart
|
||||
---
|
||||
|
||||
# adidas is all in
|
||||
As a freelancer embedded in adidas' ecommerce and digital experiences team, I worked on digital campaigns and assets.
|
||||
|
||||
Adidas, as you might have heard, is an international sports and lifestyle clothing brand. I worked on several digital campaigns as a graphic designer and illustrator.
|
||||
I created illustrations, animations, and graphics for the adidas website, social media, and newsletters.
|
||||
|
||||
|
|
|
|||
14
src/routes/work/md/booking.md
Normal file
14
src/routes/work/md/booking.md
Normal file
File diff suppressed because one or more lines are too long
26
src/routes/work/md/etosis.md
Normal file
26
src/routes/work/md/etosis.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
title: etosis
|
||||
images: ['work/etosis/etosis_og_ogqfae', 'https://res.cloudinary.com/dkvjosg5p/video/upload/v1696948648/work/etosis/etosis_mail_wawrua.webm', 'work/etosis/scrnli_Oct_10_2023_3_._xkz9er', 'work/etosis/scrnli_Oct_10_2023_2_._lxh0xh' ]
|
||||
description: Simple housestyle and website for a software company specialised in complexity
|
||||
referenceName: Patrick Simpson
|
||||
reference: https://www.linkedin.com/in/patricksimpson/
|
||||
tags: ['Housestyle', 'Graphic Design', 'Web Development', 'Animation']
|
||||
svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1136.37 547.52"><path style="fill: currentColor" d="M412.35 274.93c.19 3.41.19 6.84 0 10.25H270.51c1.9 26.58 27.34 51.18 60.29 51.18a67.587 67.587 0 0 0 52.7-25.82l20.12 20.88a97.618 97.618 0 0 1-72.82 34.47c-49.3.89-90.04-38.25-91.12-87.55v-4.56c0-51.18 34.93-92.18 87.63-92.18 48.98 0 85.35 36.83 85.35 93.32m-31.13-16.33c-2.78-27.6-26.49-48.32-54.22-47.38-28.03-.97-52.18 19.56-55.73 47.38h109.95ZM421.84 186.16h34.17v-53.15h31.89v53.15h54.67v28.55H487.9v94.69c0 17.08 8.35 26.96 25.44 26.96a42.01 42.01 0 0 0 20.12-7.59l11.77 26.2c-11 6.7-23.57 10.37-36.45 10.63-25.06 0-53.15-16.33-53.15-55.05v-95.83h-34.17l.38-28.55ZM736.66 273.79c1.11 49.86-38.41 91.18-88.27 92.3s-91.18-38.41-92.3-88.27v-4.02c-1.13-49.86 38.37-91.2 88.23-92.33 49.86-1.13 91.2 38.37 92.33 88.23v4.1m-31.89 0c0-34.55-23.16-62.57-58.39-62.57s-58.39 28.1-58.39 62.57 22.78 62.57 58.39 62.57 58.39-28.1 58.39-62.57M772.35 308.64a67.313 67.313 0 0 0 53.15 28.47c17.84 0 37.97-7.59 37.97-21.64 0-15.19-12.91-22.78-39.79-28.1-37.97-7.59-64.09-22.32-64.09-55.35 0-25.44 25.36-50.42 64.47-50.42a91.14 91.14 0 0 1 64.85 26.58l-21.03 21.72a64.366 64.366 0 0 0-43.96-19.29c-22.78 0-33.41 10.93-33.41 22.32 0 13.29 13.67 20.5 42.45 26.58 33.41 7.21 61.81 19.74 61.81 55.43 0 33.71-35.69 51.18-69.4 51.18a95.993 95.993 0 0 1-75.1-36.83l22.1-20.65ZM944.56 108.41c11.95 0 21.64 9.69 21.64 21.64s-9.69 21.64-21.64 21.64c-11.95 0-21.64-9.69-21.64-21.64 0-11.95 9.69-21.64 21.64-21.64m-15.87 77.75h31.89v175.25h-31.89V186.16ZM1013.89 308.64a67.569 67.569 0 0 0 53.15 28.47c17.77 0 37.97-7.59 37.97-21.64 0-15.19-12.91-22.78-39.79-28.1-37.97-7.59-64.09-22.32-64.09-55.35 0-25.44 25.36-50.42 64.47-50.42a91.14 91.14 0 0 1 64.85 26.58l-20.96 21.72a64.583 64.583 0 0 0-43.96-19.29c-22.4 0-33.41 10.93-33.41 22.32 0 13.29 13.67 20.5 42.45 26.58 33.41 7.21 61.81 19.74 61.81 55.43 0 33.71-35.69 51.18-69.4 51.18a95.993 95.993 0 0 1-75.1-36.83l22.02-20.65ZM0 142.65l90.11 59.43-62.71 40.93-.11.09c-4.22 3.38-7.72 7.13-10.41 11.17-10.73 16.86-6.06 38.93 10.59 50.29l63.27 40.21L0 404.19V142.65Z"/><path style="fill: currentColor" d="m68.58 314.96-32.87-21.68c-4.98-3.11-8.65-8.65-9.9-14.86-1.29-6.43.08-13.12 3.65-17.89 1.9-2.54 3.8-4.44 6.32-6.34l66.8-44.31 44.44 28.95c4.92 3.53 8.65 7.47 11.73 12.4 12.08 18.44 6.8 44.18-11.73 57.46l-44.43 28.95-34.01-22.67ZM0 420.44l102.58-67.94 44.44 28.95c4.92 3.52 8.65 7.46 11.73 12.4 12.09 19.1 6.83 44.86-11.73 57.45L0 547.52V420.44ZM0 127.07V0l147.02 96.21c4.55 3.26 8.51 7.21 11.77 11.75 5.83 9.77 7.8 20.33 5.85 31.39-1.88 10.68-8.12 19.9-17.54 25.96l-44.52 29L0 127.07Z"/></svg>'
|
||||
---
|
||||
Etosis is a small software consultancy and development company based in Helsinki, Finland that specialises in solving extremely complex software challenges.
|
||||
|
||||
## Visual Identity
|
||||
|
||||
The concept of a graphical representation of complexity was the starting point for the logo, which approximately represents the letter 'E' for etosis.
|
||||
|
||||
Both the logo and the illustrations are drawing inspiration from wavelength graphs, since a lot of the company's products are measuring and then processing such data.
|
||||
|
||||
## Website
|
||||
|
||||
When it came to designing the website, the issue was that the products of etosis mostly aren't customer facing, meaning they have abstract interfaces and applications. Therefore we had no real imagery to use, and we wanted to refrain from using overly generic photos also.
|
||||
|
||||
So I developed simplistic vector-based animations for each product that make the site interactive and interesting, even though the subject matter is rather dry.
|
||||
|
||||
The website was built in [Eleventy](https://www.11ty.dev/), a static site generator. The animations were created as SVGs and then animated with Javascript.
|
||||
|
||||
<a class="button" href="https://etosis.com">Live Site</a>
|
||||
|
|
@ -1,16 +1,25 @@
|
|||
---
|
||||
title: Formo.bio
|
||||
title: formo.bio
|
||||
header_bg_image: formo_hero_xgb6xo
|
||||
images: ['/work/formo/scrnli_screenshot_1_._k52arf', '/work/formo/scrnli_screenshot_r6cmcw', '/work/formo/scrnli_screenshot_3_._iapueq', '/work/formo/scrnli_screenshot_2_._jpypq5']
|
||||
video: /work/formo/formo-rec.webm
|
||||
order: 12
|
||||
tags: ['tech consultancy', 'web development', 'UX Design']
|
||||
description: Formo, as you might have heard, is an international sports and lifestyle clothing brand. I worked on several digital campaigns as a graphic designer and illustrator.
|
||||
description: The future of dairy on the web
|
||||
svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 70 20" style="enable-background:new 0 0 70 20" xml:space="preserve"><path d="m3.4 5.6 7.9-3.3c.1 0 .1-.1.1-.2l-.9-2c-.1-.1-.2-.1-.2-.1L2.4 3.3C.9 3.9 0 5.3 0 6.9v12.3c0 .1.1.1.1.1h2.2c.1 0 .1-.1.1-.1v-8.7c.2.1.3.1.5.2l7.2 1.8c.1 0 .1 0 .1-.1l.6-2.1c0-.1 0-.1-.1-.1L3.6 8.3C2.7 8 2.5 7.2 2.5 7s0-1 .9-1.4zm59.7.7c-3.8 0-6.9 3-6.9 6.8s3.1 6.8 6.9 6.8 6.9-3 6.9-6.8-3.1-6.8-6.9-6.8zm0 11.1c-2.4 0-4.4-1.9-4.4-4.3s2-4.3 4.4-4.3 4.4 1.9 4.4 4.3-2 4.3-4.4 4.3zM18.8 6.3c-3.8 0-6.9 3-6.9 6.8s3.1 6.8 6.9 6.8 6.9-3 6.9-6.8-3.1-6.8-6.9-6.8zm0 11.1c-2.4 0-4.4-1.9-4.4-4.3s2-4.3 4.4-4.3 4.4 1.9 4.4 4.3-2 4.3-4.4 4.3zM53.1 7.1c-.9-.7-2-.9-3-.6l-3.2.9c-.3.1-.6.2-.9.4-.2-.3-.4-.5-.7-.7-.9-.7-2-.9-3-.6l-3.3.9c-1.6.4-2.7 1.9-2.7 3.5v8.3c0 .1.1.1.1.1h2.2c.1 0 .1-.1.1-.1v-8.3c0-.5.4-1 .9-1.1l3.2-.9c.4-.1.7.1.8.2.3.2.4.5.4.8v9.4c0 .1.1.1.1.1h2.2c.1 0 .1-.1.1-.1V11c0-.5.4-1 .9-1.1l3.2-.9c.4-.1.7.1.8.2.3.2.4.5.4.8v9.4c0 .1.1.1.1.1H54c.1 0 .1 0 .1-.1V9.9c.4-1.1-.1-2.1-1-2.8zm-23 .3c-1.6.4-2.7 1.9-2.7 3.5v8.3c0 .1.1.1.1.1h2.2c.1 0 .1-.1.1-.1v-8.3c0-.5.4-1 .9-1.1l4.6-1.3c.1 0 .1-.1.1-.1l-.6-2.1c0-.1-.1-.1-.1-.1l-4.6 1.2z" style="fill:currentColor"/></svg>'
|
||||
reference: https://www.linkedin.com/in/ozollmanthomas/
|
||||
referenceName: Oscar Zollman
|
||||
agency: https://forpeople.com
|
||||
agencyName: forpeople
|
||||
---
|
||||
## A website for the future of dairy.
|
||||
Formo is using precision fermentation instead of cows to make dairy products and save the world. Creative agency [forpeople](https://forpeople.com) is in charge of branding and brought me on board to develop a brand new website.
|
||||
|
||||
Formo is using precision fermentation instead of cows to make dairy products and save the world.
|
||||
## Unlocking visual content creation
|
||||
|
||||
Formo's previous website was lacking an easy way for the team to create new, visually engaging content without technical knowledge. After consulting with the team, we decided to use the latest version of WordPress which offers full support for the powerful Gutenberg editor.
|
||||
The previous website was lacking an easy way to create new, visually engaging content without technical knowledge.
|
||||
|
||||
We built custom content blocks for the team to use and combine when creating new pages and articles.
|
||||
After consulting with the team, we decided that the comfort of the new visual editing tools of WordPress outweigh the benefits of other, more modern web frameworks, which either have data-centric approaches (and no visual editor) or require far greater effort to implement.
|
||||
|
||||
With custom-made, React-based content blocks and the Gutenberg editor, creating new pages and articles in the new formo identity framework is now easily achievable through a WYSIWYG interface.
|
||||
|
||||
<a class="button" href="https://formo.bio">Live site</a>
|
||||
21
src/routes/work/md/hatetracker.md
Normal file
21
src/routes/work/md/hatetracker.md
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,16 +1,23 @@
|
|||
---
|
||||
title: JustPeace Labs
|
||||
header_bg_image: jpl_hero_eukxaw
|
||||
description: JustPeace Labs is a non-profit organization that works with local communities to build peace and prevent violence.
|
||||
images: [ '/work/jpl/jpl_hero_eukxaw.jpg', '/work/jpl/website_01_zsk9oe.png', '/work/jpl/jpllogo_v7s2pl']
|
||||
description: Housestyle, website and design for the peacebuilding tech non-profit organisation.
|
||||
order: 11
|
||||
tags: ['branding', 'logo design', 'graphic design', 'illustration']
|
||||
tasks: ['Housestyle', 'Visual Design', 'Product Design', 'Frontend Development']
|
||||
reference: 'https://www.linkedin.com/in/jennifereasterday/'
|
||||
referenceName: 'Jennifer Easterday'
|
||||
svg: '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="Layer_1" x="0" y="0" viewBox="0 0 240 63" style="enable-background:new 0 0 240 63" xml:space="preserve"><style>.st0{fill:currentColor}.st1{fill:currentColor}</style><g id="Layer_1-2"><path class="st0" d="M82.4 23.4c.4-2.3.6-4.7.5-7.1V3.1h4.6v13.1c.1 2.5 0 5-.3 7.5-.8 4.1-4.9 6-7.4 6.1l-1.1-2.4c1.9-.6 3.3-2.1 3.7-4zM92 3.1h4.5v12.3c0 5.1 1.6 6.2 4.4 6.2s4.3-1.3 4.3-6.2V3.1h4.5v12.7c0 6.1-3 9.5-8.9 9.5-6.6 0-8.8-3.8-8.8-9.6V3.1zM114 20.3c1.7.9 3.7 1.4 5.7 1.4 2.4 0 3.2-.8 3.2-2.2s-.9-2.2-4.2-3.9c-2.6-1.3-5.4-3.2-5.4-6.8 0-4.3 3.5-5.9 7.7-5.9 2-.1 3.9.4 5.7 1.3l-.8 3.6c-1.4-.9-3.1-1.4-4.7-1.4-2.4 0-3.3.9-3.3 2.2 0 1.6 1.1 2.2 3.8 3.7 3.4 1.8 6 3.4 6 7 0 4-3.1 6.1-7.5 6.1-2.3 0-4.5-.4-6.6-1.3l.4-3.8zM135.3 6.6H129V3.1h17.2v3.5h-6.3V25h-4.6V6.6zM149.4 3.1h1.4c2.4 0 4.4-.2 6.5-.2 5 0 8 2.2 8 7.6 0 5-3.1 8-8.8 8H154V25h-4.6V3.1zm7 11.8c2.2 0 4.3-.9 4.3-4.6 0-2.8-1.5-4.3-4.4-4.2-.8 0-1.6.1-2.4.2v8.4c.8.2 1.6.2 2.5.2zM168.4 3.1h13.4v3.5H173V12h6.8v3.5H173v5.9h9.7V25h-14.3V3.1zM192.8 2.9h4.6l8.2 22.1h-5l-1.5-4.6H191l-1.5 4.6h-4.9l8.2-22.1zm5.3 14.2-2.4-7.5-.6-2.3-.6 2.3-2.4 7.5h6zM216.4 2.9c2 0 4 .3 6 .9l-.7 4c-1.5-.9-3.2-1.3-5-1.3-3.6 0-6.3 2.7-6.3 7.3s2.5 7.9 6.7 7.9c1.8 0 3.5-.5 5.1-1.4v3.8c-1.7.8-3.6 1.2-5.5 1.1-6.8 0-11-4.5-11-11.1 0-7.3 4.7-11.2 10.7-11.2zM225.7 3.1h13.4v3.5h-8.8V12h6.7v3.5h-6.7v5.9h9.7V25h-14.3V3.1z"/><path class="st1" d="M80.4 39h2.5v20H92v2H80.4V39zM98 52.1c2.1-.7 4.2-1 6.4-1v-1.9c0-2.7-1.4-3.1-3.8-3.1-1.7.1-3.4.5-4.8 1.4l-.6-1.6c1.8-1 3.9-1.6 6-1.6 3.3 0 5.6.9 5.6 5v11.9h-1c-.8 0-1.3-.1-1.3-1.2v-.6c-1.4 1.2-3.2 1.8-5.1 1.8-2.9 0-5-1.7-5-4.6 0-2.1 1.3-3.6 3.6-4.5zm2.2 7.3c1.5 0 3-.5 4.2-1.4v-5.3c-1.7 0-3.4.2-5.1.8-1.6.6-2.5 1.5-2.5 3 0 1.7 1 2.9 3.4 2.9zM112 37h2.4v6.8l-.1 2.5c1.3-1.3 3.1-2 5-2 3.6 0 6.5 2.6 6.5 7.8s-3.3 9.1-8.3 9.1c-1.9 0-3.8-.4-5.5-1.2V37zm5.8 22.4c2.9 0 5.5-2.3 5.6-7 0-4.1-1.6-6.3-4.7-6.3-1.6 0-3.1.6-4.2 1.7v10.9c.9.6 2.1.8 3.3.7zM129.4 58.2c1.5.8 3.1 1.2 4.7 1.2 2.1 0 3.6-.8 3.6-2.5s-2-2.5-3.9-3.3c-2.5-1-4.7-2.4-4.7-5.1s2.7-4.1 5.7-4.2c1.5 0 2.9.3 4.2.9l-.4 1.8c-1.1-.6-2.4-.9-3.7-.9-2.4 0-3.6.9-3.6 2.2 0 1.7 1.4 2.5 3.6 3.4s5 2.2 5 5-2.2 4.5-5.7 4.5c-1.8 0-3.6-.4-5.2-1.2l.4-1.8zM7.2 31.7c0-12.2 8.9-22.6 21-24.4L27.1 0C11.6 2.4.1 15.7 0 31.5c0 6.8 2.2 13.5 6.2 19l5.8-4.3C8.9 42 7.2 36.9 7.2 31.7zM17.1 51.4l-4.2 5.7c4.2 3.1 9.1 5.1 14.2 5.9l1-7c-4-.6-7.8-2.2-11-4.6zM36.5 0l-1.1 7.3c13.5 2 22.7 14.5 20.7 28-.6 3.9-2.1 7.7-4.5 10.9l5.7 4.2c4.1-5.5 6.3-12.1 6.2-19 .1-15.7-11.4-29-27-31.4zM35.5 56l1 7c5.2-.8 10-2.8 14.2-5.9l-4.2-5.7c-3.2 2.4-7 4-11 4.6z"/></g></svg>'
|
||||
---
|
||||
|
||||
## JPL's Mission
|
||||
The startup has declared their mission "to empower local communities to be active participants in creating lasting just peace through the use of technology". I created a logo and housestyle for them that conveyes their goals and products.
|
||||
JustPeace Labs is a non-profit organization that works with local communities to build peace and prevent violence.
|
||||
|
||||
## The "broken peace" logo
|
||||
Since 1958, the Symbol for the British nuclear disarmament movement by Gerald Holtom is used as an international symbol for peace.
|
||||
## Housestyle
|
||||
I created a logo and housestyle for JustPeace Labs that conveyes their goals and products.
|
||||
|
||||
Clients of JustPeace Labs are peace builders. The logo is a 'broken' peace sign with only the frame intact. It stands for the technological and ethical frameworks that JPL aims to provide that can be used to achieve long lasting, just peace.
|
||||
Clients of JustPeace Labs are peace builders. The logo is a 'broken' peace sign with only the frame intact. It stands for the technological and ethical frameworks that JPL aims to provide that can be used to achieve long lasting, just peace.
|
||||
|
||||
## Projects
|
||||
|
||||
I worked alongside the founders of the NGO and other peace building initiatives to develop communication guidelines and frameworks as well as websites and apps such as the hate crime reporting tool [Hate tracker](/work/hatetracker).
|
||||
12
src/routes/work/md/letshost.md
Normal file
12
src/routes/work/md/letshost.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
title: Letshost.ie
|
||||
description: Design and development for the irish hosting provider
|
||||
images: ['work/letshost/letshost_hero_ibd4sx', 'work/letshost/letshost_sharedhosting_desktop_sriv5t', 'work/letshost/letshost_testimonials_desktop_ijwxmy', 'work/letshost/letshost_wordpress_desktop_qaknlm']
|
||||
tags: ['Graphc Design', 'Web Design', 'Frontend Development']
|
||||
reference: https://www.linkedin.com/in/daraghmacloughlin/
|
||||
referenceName: Daragh Mac Loughlin
|
||||
svg: '<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 115.2 16.4"><path fill="currentColor" d="M54.4.5H58v6.2h5.9V.5h3.6v15.6h-3.6v-6H58v6h-3.6V.5zM12.6 16.1V.5h11.1v3.3h-7.4v2.9H23v3.2h-6.7v2.9h7.8v3.4l-11.5-.1zM51.3 2.3l-1.7 2.4c-1.4-.5-2.6-.9-3.8-1.3-.4-.1-.9-.1-1.3.1s-.8.7-.8 1.1c.1.5.4.9.8 1.2.8.3 1.6.6 2.4.8.8.2 1.5.5 2.3.8 3.2 1.4 3.2 6 .6 7.8-3.2 2.2-8.2.9-10.6-1.8l2.1-2.6c.2.2.4.3.7.5 1.1.6 2.2 1.2 3.4 1.6.5.2 1 .1 1.5-.1.4-.2.7-.6.9-1 .2-.5-.1-1.1-.6-1.3h-.1c-.8-.3-1.5-.5-2.3-.8s-1.7-.5-2.5-.9c-1.4-.6-2.3-1.9-2.4-3.4-.3-1.7.5-3.5 1.9-4.4 3.1-2 7.1-.9 9.3.9.1.2.2.3.2.4zM88.9 13.6l2.2-2.7c.3.2.5.4.8.6 1 .6 2 1.1 3.1 1.5.5.2 1.1.1 1.6-.1s.8-.6.9-1.1c0-.5-.3-1-.7-1.2-.7-.3-1.4-.6-2.1-.7-.9-.3-1.8-.6-2.6-.9-1.6-.6-2.7-2.3-2.5-4-.1-1.7.9-3.3 2.4-4.1 2.7-1.4 7.2-.7 9.1 1.5l-1.7 2.4c-1.2-.5-2.4-.9-3.6-1.3-.5-.1-1-.1-1.5.1s-.8.6-.9 1.1c0 .5.3 1 .8 1.2.8.3 1.6.6 2.4.8.8.2 1.5.5 2.3.8 1.7.7 2.7 2.3 2.6 4.1 0 1.8-1 3.4-2.7 4.1-2.3 1.1-4.9 1-7.1-.3-1-.6-1.9-1.2-2.8-1.8zM38.1.5v3.1h-4.3v12.5h-3.7V3.6h-4.3V.5h12.3zM110.8 16.1h-3.6V3.7h-4.3V.6h12.3v3h-4.4v12.5zM3.7 12.8h6.6v3.3H0V.5h3.7v12.3zM86.2 5.9C85.4 3.7 83.7 2 81.6 1c-.1.1-3.1 1.3-4.7 3.6 2.2-.5 4-.1 5.4.6 1.8 1.1 3.2 2.8 4 4.8.3-1.2.3-2.7-.1-4.1z"/><path fill="currentColor" d="M81 15.8c2.2-.8 3.9-2.5 4.9-4.6-.1-.1-1.3-3.1-3.6-4.7.5 2.2.1 4-.6 5.4-1.1 1.8-2.8 3.2-4.8 4 1.3.3 2.8.3 4.1-.1z"/><path fill="currentColor" d="M71.1 10.7c.8 2.2 2.5 3.9 4.6 4.9.1-.1 3.1-1.3 4.7-3.6-2.2.5-4 .1-5.4-.6-1.8-1.1-3.2-2.8-4-4.8-.3 1.2-.3 2.7.1 4.1z"/><path fill="currentColor" d="M76.3.8c-2.2.8-3.9 2.5-4.9 4.6.1.1 1.3 3.1 3.6 4.7-.5-2.2-.1-4 .6-5.4 1.1-1.8 2.8-3.2 4.8-4-1.3-.3-2.8-.3-4.1.1z"/></svg>'
|
||||
---
|
||||
LetsHost.ie is the leading irish webhosting provider. I worked with the marketing team to redesign their website and improve their user journeys.
|
||||
|
||||
Their best selling product are WordPress hosting packages. So in order to really practice what they preach and showcase what is possible in terms of performance and design, their own website also runs on the same stack.
|
||||
|
|
@ -1,9 +1,18 @@
|
|||
---
|
||||
title: peak.capital
|
||||
header_bg_image: peak_hero_ewp1wb
|
||||
images: ['/work/peak/peak-mobile-mockup_wtp3ea', '/work/peak/peak_hero_ewp1wb']
|
||||
order: 12
|
||||
description: Peak Capital description
|
||||
tags: ['early-stage investment fund', 'venture capital', 'fund', 'investor']
|
||||
svg: '<svg class="peak-logo-svg" xmlns="http://www.w3.org/2000/svg" viewBox="-1 -1 102 102"><style>.peak-logo-svg-circle{fill: currentColor}.peak-logo-svg-letters{fill:var(--color-bg)}</style><circle class="peak-logo-svg-circle" cx="50" cy="50" r="50"></circle><polygon class="peak-logo-svg-letters" points="70.45 39.19 70.45 61.48 75.19 61.48 75.19 53.82 78.47 50.48 84.61 61.48 90.66 61.48 82.14 46.79 89.65 39.19 83.69 39.19 75.19 47.68 75.19 39.19 70.45 39.19"></polygon><polygon class="peak-logo-svg-letters" points="43.83 43.41 43.83 39.22 28.98 39.22 28.98 61.48 43.83 61.48 43.83 57.29 33.71 57.29 33.71 52.23 43.2 52.23 43.2 48.03 33.71 48.03 33.71 43.41 43.83 43.41"></polygon><path class="peak-logo-svg-letters" d="M45.77,61.48h5.12L51.48,60a3,3,0,0,1,.16-.36,6,6,0,0,1,11-.21,2.92,2.92,0,0,1,.14.31l.7,1.78h5.15l-8.9-22.26h-5Zm13.95-9.63a10.56,10.56,0,0,0-2.53-.31,9.85,9.85,0,0,0-2.55.33l2.56-6.52Z"></path><path class="peak-logo-svg-letters" d="M9.34,39.22V61.48h4.73V53.57h3.4c5.56,0,9-2.71,9-7.18s-3.46-7.17-9-7.17Zm12.29,7.17c0,1.91-1.48,3-4.16,3h-3.4v-6h3.4C20.15,43.41,21.63,44.49,21.63,46.39Z"></path></svg>'
|
||||
description: A new website for the european investment fund
|
||||
tags: ['Webdesign', 'Frontend Development']
|
||||
svg: '<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 97 97"><path style="fill:currentColor" fill-rule="evenodd" d="M.1 48.5C.1 21.7 21.8 0 48.6 0 75.3.1 97 21.8 97 48.5 97 75.3 75.3 97 48.5 97S.1 75.3.1 48.5zm14.5 3.2H18c5.4 0 8.8-2.6 8.8-7s-3.4-7-8.8-7h-8v21.7h4.6v-7.7zm19.2-9.9h9.9v-4.1H29.2v21.7h14.5v-4.1h-9.9v-4.9h9.3v-4.1h-9.3v-4.5zm28.4 15.9.7 1.7h5l-8.7-21.7h-4.9l-8.7 21.7h5l.6-1.5c0-.1.1-.2.2-.4 1.1-2.2 3.2-3.6 5.4-3.6s4.2 1.3 5.3 3.4c0 .1 0 .3.1.4zm12.1-5.8 3.2-3.3 6 10.7h5.9L81.1 45l7.3-7.4h-5.8l-8.3 8.3v-8.3h-4.6v21.8h4.6v-7.5zM18 41.8h-3.3v5.8H18c2.6 0 4.1-1 4.1-2.9-.1-1.9-1.5-2.9-4.1-2.9zM54.2 50c.8-.2 1.6-.3 2.5-.3.8 0 1.7.1 2.5.3l-2.5-6.3-2.5 6.3z" clip-rule="evenodd"/></svg>'
|
||||
referenceName: Madeline Lawrence
|
||||
reference: https://www.linkedin.com/in/madelinelawren/
|
||||
---
|
||||
Peak is an early-stage investment fund backed by entrepreneurs in Europe.
|
||||
Peak is an early-stage investment fund backed by entrepreneurs in Europe.
|
||||
|
||||
In 2021 the creative agency [forpeople](https://forpeople.com) helped Peak with a full rebranding. They brought me on board to help translate the new branding to the company's exist ing WordPress website.
|
||||
|
||||
Over the past years I have worked with the team at peak to develop the site further.
|
||||
|
||||
<a class="button" href="https://peak.capital">Live site</a>
|
||||
|
|
@ -1,10 +1,20 @@
|
|||
---
|
||||
title: Uncommonbio.co
|
||||
title: uncommonbio.co
|
||||
header_bg_image: uncommon_hero_jf1in9
|
||||
images: ['work/uncommon/uncommon_hero_jf1in9', 'work/uncommon/scrnli_Oct_10_2023_sspigq', 'work/uncommon/scrnli_screenshot_4_._dctvo7', 'work/uncommon/scrnli_Oct_10_2023_1_._ybvayx']
|
||||
order: 12
|
||||
tags: ['tech consultancy', 'web development', 'UX Design', 'Knoterputer', 'Henkenstein']
|
||||
description: Peak Capital description
|
||||
tags: ['Tech consultancy', 'Frontend development',]
|
||||
description: A brand-new website for the home of animal-free bacon
|
||||
reference: https://www.linkedin.com/in/patrick-niall-38560b1b/
|
||||
referenceName: Patrick Niall
|
||||
agency: https://forpeople.com
|
||||
agencyName: forpeople
|
||||
svg: '<svg id="uncommonLogo" x="0" y="0" viewBox="0 0 932 110.7" style="enable-background:new 0 0 932 110.7; fill: currentColor;"><path id="n7" class="uncommonLogo-letter" d="M828.9 3.5h30.5v9.6C865.6 4.9 878.2 0 889.3 0c10.2 0 21.6 3.1 28.1 9.1 8.4 7.5 14.7 19.9 14.7 32v66.4h-30.3V48.8c0-5-1.8-12.9-4.6-15.7-2.7-2.8-6.7-6.8-16.2-6.8s-14.8 4.9-17.5 8.9c-2.3 3.3-4.1 7.4-4.1 13.6v58.8h-30.5V3.5z"></path><path id="o6" class="uncommonLogo-letter" d="M801.2 17.2c-10.8-10.7-24.1-16-39.6-16s-28.7 5.3-39.5 16C711.4 27.7 706 40.6 706 56c0 15.3 5.4 28.3 16.1 39 10.7 10.5 23.9 15.8 39.5 15.8s28.8-5.3 39.6-16C812 84.3 817.5 71.3 817.5 56c0-15.4-5.4-28.3-16.3-38.8zm-21.7 57.1c-4.9 5-10.8 7.5-17.9 7.5s-13-2.5-17.7-7.5c-4.9-5-7.3-11.1-7.3-18.4 0-7.2 2.4-13.4 7.1-18.4 4.9-5 10.8-7.5 17.9-7.5s13 2.5 17.9 7.5c4.9 5 7.3 11.1 7.3 18.4 0 7.3-2.5 13.4-7.3 18.4z"></path><path id="mm5" class="uncommonLogo-letter" d="M446.9 3.5h30.5v9.6C483.6 4.9 496.2 0 507.3 0c10.2 0 21.6 3.1 28.1 9.1 3.3 2.9 6.3 6.7 8.7 10.8 3.7-4.2 6.3-6.8 6.3-6.8C557.1 6.2 569.1 0 580.2 0c10.2 0 21.6 3.1 28.1 9.1 3.3 3 6.3 6.7 8.7 10.9 3.7-4.2 6.4-6.9 6.4-6.9C630.1 6.2 642.1 0 653.2 0c10.2 0 21.6 3.1 28.1 9.1 8.4 7.5 14.7 19.9 14.7 32v66.4h-30.3V48.7c0-5-1.8-12.9-4.6-15.7-2.7-2.8-6.7-6.8-16.2-6.8s-14.8 4.9-17.5 8.9c-2.3 3.3-4.1 7.4-4.1 13.6v58.8h-30.7V48.7c0-5-1.8-12.9-4.6-15.7-2.7-2.8-6.7-6.8-16.2-6.8s-14.8 4.9-17.5 8.9c-2.3 3.3-4.1 7.4-4.1 13.6v58.8h-30.6V48.7c0-5-1.8-12.9-4.6-15.7-2.7-2.8-6.7-6.8-16.2-6.8s-14.8 4.9-17.5 8.9c-2.3 3.3-4.1 7.4-4.1 13.6v58.8h-30.5V3.5z"></path><path id="o4" class="uncommonLogo-letter" d="M420 17.2c-10.8-10.7-24.1-16-39.6-16s-28.7 5.3-39.5 16c-10.7 10.5-16.1 23.5-16.1 38.8 0 15.3 5.4 28.3 16.1 39 10.7 10.5 23.9 15.8 39.5 15.8s28.8-5.3 39.6-16c10.8-10.5 16.3-23.5 16.3-38.8 0-15.4-5.4-28.3-16.3-38.8zm-21.7 57.1c-4.9 5-10.8 7.5-17.9 7.5s-13-2.5-17.7-7.5c-4.9-5-7.3-11.1-7.3-18.4 0-7.2 2.4-13.4 7.1-18.4 4.9-5 10.8-7.5 17.9-7.5s13 2.5 17.9 7.5c4.9 5 7.3 11.1 7.3 18.4 0 7.3-2.4 13.4-7.3 18.4z"></path><path id="c3" class="uncommonLogo-letter" d="M247.1 16.8c10.4-10.4 23.4-15.6 39.1-15.6 13.6 0 25.5 4.5 35.6 13.4l-11.3 26.5c-6.8-7.3-14.6-11-23.2-11-7.2 0-13.2 2.5-18.1 7.5-4.9 4.9-7.3 11.2-7.3 18.9 0 7.6 2.4 13.7 7.1 18.4 4.9 4.7 10.8 7 17.9 7 8.9 0 16.7-3.7 23.6-11l11.3 26.5c-10.1 8.9-22 13.4-35.6 13.4-15.7 0-28.8-5.2-39.3-15.6-10.4-10.4-15.5-23.5-15.5-39.1s5.2-28.9 15.7-39.3z"></path><path id="n2" class="uncommonLogo-letter" d="M117.8 3.5h30.5v9.6C154.5 4.9 167.1 0 178.2 0c10.2 0 21.6 3.1 28.1 9.1 8.4 7.5 14.7 19.9 14.7 32v66.4h-30.3V48.7c0-5-1.8-12.9-4.6-15.7-2.7-2.8-6.7-6.8-16.2-6.8s-14.8 4.9-17.5 8.9c-2.3 3.3-4.1 7.4-4.1 13.6v58.8h-30.5V3.5z"></path><path id="u1" class="uncommonLogo-letter" d="M103.1 107.2H72.6v-9.6c-6.2 8.2-18.8 13.1-29.9 13.1-10.2 0-21.6-3.1-28.1-9.1C6.3 94.1 0 81.7 0 69.6V3.2h30.3V62c0 5 1.8 12.9 4.6 15.7 2.7 2.8 6.7 6.8 16.2 6.8s14.8-4.9 17.5-8.9c2.3-3.3 4.1-7.4 4.1-13.6V3.2h30.5v104z"></path></svg>'
|
||||
---
|
||||
Uncommon's mission is to sell rashers and rashers of cultivated pork at price parity.
|
||||
For happy pigs and piggy banks.
|
||||
Uncommon's mission is to sell rashers and rashers of cultivated pork at price parity. For happy pigs and piggy banks.
|
||||
|
||||
Another project in collaboration with the ever-inspiring creative agency [forpeople](https://forpeople.com). Their client Uncommon needed a website to go along with their new brand name and identity.
|
||||
|
||||
Unique features include an option to have your website browsing pleasure be accompanied by the sound of bacon sizzling in a pan and a page that explains the science of the product that adapts to your knowledge level.
|
||||
|
||||
<a href="https://uncommonbio.co" class="button" target="_blank">Check it out live</a>
|
||||
71
src/routes/work/old+page.svelte
Normal file
71
src/routes/work/old+page.svelte
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<script lang="ts">
|
||||
import WorkCanvas from '$lib/components/WorkCanvas.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { workClickHandler, initWorkPage } from './workUtils.js';
|
||||
import { workbulge } from '$lib/utils/stores.js';
|
||||
import { CldImage } from 'svelte-cloudinary';
|
||||
export let data;
|
||||
|
||||
const orderedPosts = data.posts.sort((a, b) => {
|
||||
return a.meta.order - b.meta.order;
|
||||
});
|
||||
|
||||
let canvasTextElems: Array<HTMLElement>;
|
||||
let canvasImgElems: Array<HTMLElement>;
|
||||
let bulge = {factor: 0};
|
||||
|
||||
workbulge.subscribe((val) => {
|
||||
bulge.factor = val;
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
const headline: HTMLElement = document.querySelector('.headline') as HTMLElement;
|
||||
const headlines: Array<HTMLElement> = Array.from(document.querySelectorAll('h2, li'));
|
||||
const images: Array<HTMLElement> = Array.from(document.querySelectorAll('.work img'));
|
||||
let canvasElems = initWorkPage( headlines, images );
|
||||
canvasTextElems = canvasElems.text as Array<HTMLElement>;
|
||||
canvasImgElems = canvasElems.images;
|
||||
});
|
||||
</script>
|
||||
<div class="works-wrapper">
|
||||
|
||||
<h1 class="headline"><span>Outstanding work</span></h1>
|
||||
<div class="works">
|
||||
|
||||
{#each orderedPosts as work, i}
|
||||
<a
|
||||
data-sveltekit-preload-data
|
||||
href="{work.path}"
|
||||
class="work"
|
||||
on:click={ (e) => workClickHandler(e) }
|
||||
>
|
||||
<CldImage
|
||||
src={work.meta.header_bg_image}
|
||||
sizes={ i === 0 ? `(min-width: 768px) 60vw, 50vw` : `(min-width: 768px) 20vw, 50vw`}
|
||||
alt={work.meta.title}
|
||||
width="2100"
|
||||
height="1400"
|
||||
objectFit="fill"
|
||||
loading= "lazy"
|
||||
/>
|
||||
<div class="work-info">
|
||||
<h2 class="title"><span class="title-words">{work.meta.title}</span></h2>
|
||||
{#if work.meta.tags}
|
||||
<ul class="tags">
|
||||
{#each work.meta.tags as tag, index}
|
||||
<li class="tag">{tag}{index < work.meta.tags.length-1 ? ' | ' : ''}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
</div>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
<WorkCanvas
|
||||
textsToCanvas={canvasTextElems}
|
||||
imgsToCanvas={canvasImgElems}
|
||||
bulgeFactor={bulge.factor}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style src="./work.scss" lang="scss"></style>
|
||||
113
src/routes/work/old-work.scss
Normal file
113
src/routes/work/old-work.scss
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
.works-wrapper {
|
||||
max-width: 1200px;
|
||||
margin: auto;
|
||||
}
|
||||
h1 {
|
||||
font-size: 16vw;
|
||||
margin: var(--spacing-nav) var(--spacing-nav) .25em var(--spacing-nav);
|
||||
padding: 0 0 calc(var(--spacing-nav) / 3) 0;
|
||||
position: relative;
|
||||
z-index: -1;
|
||||
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
border-bottom: 1px solid;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
font-size: 7vw;
|
||||
}
|
||||
}
|
||||
.works {
|
||||
padding: 0.5em;
|
||||
display: flex;
|
||||
gap: 0.25em;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1px;
|
||||
padding: 0 var(--spacing-nav);
|
||||
margin: 0 auto;
|
||||
padding-bottom: 20svh;
|
||||
}
|
||||
}
|
||||
.work-info,
|
||||
.work:visited .work-info {
|
||||
position: absolute;
|
||||
left: 0.25em;
|
||||
top: 0.25em;
|
||||
padding: 0.5em .75em;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
background-color: var(--color-text);
|
||||
visibility: hidden;
|
||||
|
||||
& h2 {
|
||||
text-transform: none;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 1.2em;
|
||||
letter-spacing: -0.02em;
|
||||
line-height: 1;
|
||||
visibility: hidden;
|
||||
color: var(--color-bg);
|
||||
}
|
||||
& .tags {
|
||||
visibility: hidden;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 10px 0 0 0 ;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.15em;
|
||||
transition: all .4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
& .tag {
|
||||
font-size: .6em;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.01em;
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
border-radius: 3px;
|
||||
text-transform: uppercase;
|
||||
// font-weight: 800;
|
||||
// font-style: italic;
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
.work:hover .work-info {
|
||||
background-color: var(--color-bg);
|
||||
& h2 {
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
.work {
|
||||
flex: 0 0 calc(50% - 0.125em );
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
// transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
grid-column-end: span 1;
|
||||
|
||||
&:first-child {
|
||||
grid-column-end: span 2;
|
||||
grid-row-end: span 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
.work .tag {
|
||||
opacity: 0;
|
||||
}
|
||||
:global(.work img) {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
aspect-ratio: var(--aspect-ratio-heroes);
|
||||
object-fit: fill;
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
}
|
||||
|
|
@ -1,113 +1,86 @@
|
|||
.works-wrapper {
|
||||
max-width: 1200px;
|
||||
margin: auto;
|
||||
}
|
||||
h1 {
|
||||
font-size: 16vw;
|
||||
margin: var(--spacing-nav) var(--spacing-nav) .25em var(--spacing-nav);
|
||||
padding: 0 0 calc(var(--spacing-nav) / 3) 0;
|
||||
position: relative;
|
||||
z-index: -1;
|
||||
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
border-bottom: 1px solid;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
font-size: 7vw;
|
||||
}
|
||||
}
|
||||
.works {
|
||||
padding: 0.5em;
|
||||
display: flex;
|
||||
gap: 0.25em;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1px;
|
||||
padding: 0 var(--spacing-nav);
|
||||
margin: 0 auto;
|
||||
padding-bottom: 20svh;
|
||||
}
|
||||
}
|
||||
.work-info,
|
||||
.work:visited .work-info {
|
||||
position: absolute;
|
||||
left: 0.25em;
|
||||
top: 0.25em;
|
||||
padding: 0.5em .75em;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
background-color: var(--color-text);
|
||||
visibility: hidden;
|
||||
|
||||
& h2 {
|
||||
text-transform: none;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 1.2em;
|
||||
letter-spacing: -0.02em;
|
||||
line-height: 1;
|
||||
visibility: hidden;
|
||||
color: var(--color-bg);
|
||||
}
|
||||
& .tags {
|
||||
visibility: hidden;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 10px 0 0 0 ;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.15em;
|
||||
transition: all .4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
& .tag {
|
||||
font-size: .6em;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.01em;
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
border-radius: 3px;
|
||||
text-transform: uppercase;
|
||||
// font-weight: 800;
|
||||
// font-style: italic;
|
||||
color: var(--color-text);
|
||||
}
|
||||
perspective: 700px;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
.work:hover .work-info {
|
||||
background-color: var(--color-bg);
|
||||
& h2 {
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
.work {
|
||||
flex: 0 0 calc(50% - 0.125em );
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
// transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
.headline {
|
||||
position: fixed;
|
||||
pointer-events: none;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
letter-spacing: -0.05em;
|
||||
z-index: 10;
|
||||
transform: translateZ(500px);
|
||||
margin-left: 40.5vw;
|
||||
margin-top: 40vh;
|
||||
font-size: 14vw;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
grid-column-end: span 1;
|
||||
|
||||
&:first-child {
|
||||
grid-column-end: span 2;
|
||||
grid-row-end: span 2;
|
||||
}
|
||||
margin-left: 41.66vw;
|
||||
margin-top: 40.5vh;
|
||||
font-size: 7vw;
|
||||
transform: translateZ(600px);
|
||||
}
|
||||
}
|
||||
.work .tag {
|
||||
opacity: 0;
|
||||
.works {
|
||||
margin: 0 auto;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 600vw;
|
||||
height: 600vw;
|
||||
transform: scale(.333);
|
||||
transform-origin: 0 0;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, 100vw);
|
||||
grid-auto-rows: 100vh;
|
||||
// grid-template-rows: repeat(6, 100vh);
|
||||
// grid-auto-flow: column;
|
||||
gap: 6px;
|
||||
}
|
||||
:global(.work img) {
|
||||
.work {
|
||||
background-color: var(--color-highlight);
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
opacity: 0;
|
||||
transform: translateZ(700px);
|
||||
}
|
||||
.work-logo {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
aspect-ratio: var(--aspect-ratio-heroes);
|
||||
height: 100%;
|
||||
color: var(--color-bg);
|
||||
padding: 3em;
|
||||
margin: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
:global(.work-logo svg) {
|
||||
object-fit: fill;
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.work-info {
|
||||
display: none;
|
||||
}
|
||||
.work-info .tags {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
gap: .25em;
|
||||
}
|
||||
|
|
@ -54,7 +54,7 @@ export function initWorkPage( canvasTextElems: Array<HTMLElement>, canvasImgElem
|
|||
ease: 'power2.inOut',
|
||||
overwrite: true
|
||||
})
|
||||
})
|
||||
}, { passive: true })
|
||||
workinfo.parentElement?.addEventListener('mouseleave', () => {
|
||||
gsap.to(workinfo, {
|
||||
duration: .25,
|
||||
|
|
@ -63,7 +63,7 @@ export function initWorkPage( canvasTextElems: Array<HTMLElement>, canvasImgElem
|
|||
ease: 'power2.inOut',
|
||||
overwrite: true
|
||||
})
|
||||
})
|
||||
}, { passive: true })
|
||||
})
|
||||
|
||||
gsap.registerPlugin( SplitText );
|
||||
|
|
@ -76,12 +76,12 @@ export function initWorkPage( canvasTextElems: Array<HTMLElement>, canvasImgElem
|
|||
const target = e.target as HTMLElement;
|
||||
gsap.to(target?.querySelectorAll('.tag'), {duration: 0.5, opacity: 1, yPercent: 0, ease: 'back.out(1.7)', stagger: 0.025, overwrite: true});
|
||||
gsap.to(target?.querySelector('h2'), {duration: 0.5, opacity: 1, ease: 'back.out(1.7)', overwrite: true});
|
||||
});
|
||||
}, { passive: true });
|
||||
el.addEventListener('mouseleave', (e) => {
|
||||
const target = e.target as HTMLElement;
|
||||
gsap.to(target?.querySelectorAll('.tag'), {duration: 0.3, opacity: 0, yPercent: 200, ease: 'power2.out', overwrite: true});
|
||||
gsap.to(target?.querySelector('h2'), {duration: 0.5, opacity: 1, ease: 'back.out(1.7)', overwrite: true});
|
||||
});
|
||||
}, { passive: true });
|
||||
});
|
||||
|
||||
gsap.set('.work', {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
%PDF-1.6
%âãÏÓ
|
||||
1 0 obj
<</Metadata 2 0 R/OCProperties<</D<</ON[21 0 R]/Order 22 0 R/RBGroups[]>>/OCGs[21 0 R]>>/Pages 3 0 R/Type/Catalog>>
endobj
2 0 obj
<</Length 35384/Subtype/XML/Type/Metadata>>stream
|
||||
1 0 obj
<</Metadata 2 0 R/OCProperties<</D<</ON[21 0 R]/Order 22 0 R/RBGroups[]>>/OCGs[21 0 R]>>/Pages 3 0 R/Type/Catalog>>
endobj
2 0 obj
<</Length 35379/Subtype/XML/Type/Metadata>>stream
|
||||
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
|
||||
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 9.1-c001 79.675d0f7, 2023/06/11-19:21:16 ">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
|
|
@ -23,9 +23,9 @@
|
|||
</rdf:Alt>
|
||||
</dc:title>
|
||||
<xmp:CreatorTool>Adobe Illustrator 27.9 (Macintosh)</xmp:CreatorTool>
|
||||
<xmp:CreateDate>2023-09-25T16:48:59+02:00</xmp:CreateDate>
|
||||
<xmp:ModifyDate>2023-09-25T16:49:00+02:00</xmp:ModifyDate>
|
||||
<xmp:MetadataDate>2023-09-25T16:49:00+02:00</xmp:MetadataDate>
|
||||
<xmp:CreateDate>2023-10-10T10:25:16+02:00</xmp:CreateDate>
|
||||
<xmp:ModifyDate>2023-10-10T10:25:16+02:00</xmp:ModifyDate>
|
||||
<xmp:MetadataDate>2023-10-10T10:25:16+02:00</xmp:MetadataDate>
|
||||
<xmp:Thumbnails>
|
||||
<rdf:Alt>
|
||||
<rdf:li rdf:parseType="Resource">
|
||||
|
|
@ -37,14 +37,14 @@
|
|||
</rdf:Alt>
|
||||
</xmp:Thumbnails>
|
||||
<xmpMM:OriginalDocumentID>uuid:C1BCCE1871B8DB11993190FCD52B4E9F</xmpMM:OriginalDocumentID>
|
||||
<xmpMM:DocumentID>xmp.did:58fe207a-83bc-43b3-9232-7fe13300d983</xmpMM:DocumentID>
|
||||
<xmpMM:InstanceID>uuid:8133d5b2-fd8d-6f44-960f-df0bd506695c</xmpMM:InstanceID>
|
||||
<xmpMM:DocumentID>xmp.did:b1ec28c0-ec5e-41e8-bb90-900b52a6183d</xmpMM:DocumentID>
|
||||
<xmpMM:InstanceID>uuid:ecb72b8d-2b96-f64d-8dd3-1771e30e5ea2</xmpMM:InstanceID>
|
||||
<xmpMM:RenditionClass>proof:pdf</xmpMM:RenditionClass>
|
||||
<xmpMM:DerivedFrom rdf:parseType="Resource">
|
||||
<stRef:instanceID>xmp.iid:6f84dae2-d69b-42ed-bad4-e5a2afd78ab2</stRef:instanceID>
|
||||
<stRef:documentID>xmp.did:6f84dae2-d69b-42ed-bad4-e5a2afd78ab2</stRef:documentID>
|
||||
<stRef:instanceID>uuid:ee8bd520-59c3-6946-b606-02f20bc24563</stRef:instanceID>
|
||||
<stRef:documentID>xmp.did:58fe207a-83bc-43b3-9232-7fe13300d983</stRef:documentID>
|
||||
<stRef:originalDocumentID>uuid:C1BCCE1871B8DB11993190FCD52B4E9F</stRef:originalDocumentID>
|
||||
<stRef:renditionClass>proof:pdf</stRef:renditionClass>
|
||||
<stRef:renditionClass>default</stRef:renditionClass>
|
||||
</xmpMM:DerivedFrom>
|
||||
<xmpMM:History>
|
||||
<rdf:Seq>
|
||||
|
|
@ -57,8 +57,8 @@
|
|||
</rdf:li>
|
||||
<rdf:li rdf:parseType="Resource">
|
||||
<stEvt:action>saved</stEvt:action>
|
||||
<stEvt:instanceID>xmp.iid:58fe207a-83bc-43b3-9232-7fe13300d983</stEvt:instanceID>
|
||||
<stEvt:when>2023-09-25T16:48:58+02:00</stEvt:when>
|
||||
<stEvt:instanceID>xmp.iid:b1ec28c0-ec5e-41e8-bb90-900b52a6183d</stEvt:instanceID>
|
||||
<stEvt:when>2023-09-25T16:49:34+02:00</stEvt:when>
|
||||
<stEvt:softwareAgent>Adobe Illustrator 27.9 (Macintosh)</stEvt:softwareAgent>
|
||||
<stEvt:changed>/</stEvt:changed>
|
||||
</rdf:li>
|
||||
|
|
@ -569,87 +569,97 @@
|
|||
|
||||
|
||||
|
||||
<?xpacket end="w"?>
endstream
endobj
3 0 obj
<</Count 1/Kids[5 0 R]/Type/Pages>>
endobj
5 0 obj
<</ArtBox[0.000152588 0.000518799 1000.0 1000.0]/BleedBox[0.0 0.0 1000.0 1000.0]/Contents 23 0 R/CropBox[0.0 0.0 1000.0 1000.0]/LastModified(D:20230925164859+02'00')/MediaBox[0.0 0.0 1000.0 1000.0]/Parent 3 0 R/Resources<</ColorSpace<</CS0 24 0 R>>/ExtGState<</GS0 25 0 R>>/Properties<</MC0 21 0 R>>>>/Thumb 26 0 R/TrimBox[0.0 0.0 1000.0 1000.0]/Type/Page/PieceInfo<</Illustrator 8 0 R>>>>
endobj
23 0 obj
<</Filter/FlateDecode/Length 349>>stream
|
||||
H‰lRËN1¼ç+üqãGçJAœ*TqàV€TúÿΣÐZwãdÆãqvO{Øö îî÷Î!¥´Bñû5¼ÀWØíŸlHã<48>Ëæ©GO½]Âh$ Zkè¯AN ̰}†¾Õc$1L• æŒ(D.2¤U+DÉ‚9l!²lÜ ª*&ɾYhfr÷ül‘N´rÕ9\Dz=*M<>Õ:½dµiâR“¹ˆÊHýgõ¬<C3B5>Š×r°OG2£TÝ„ÐØ<0C>f¬(®],a–ÚUª+Po4uÁ…k¶L‘K·”‚L4óì}–¦àF º‘²Ÿzßæ69 7TkÀ5¡wÏÕeùá)õŽ7ÓX#òoç<6F>½¶Y¨’»Æ¶¦~]ma®|„÷ÿ©Ç'ß’ÈGø»DWì´ÓÏÓ²öZÆï×)<üöà | ||||