Skip to content

Commit

Permalink
Add work project
Browse files Browse the repository at this point in the history
  • Loading branch information
lukebrooker committed Apr 14, 2024
1 parent f7dd077 commit 470eb48
Show file tree
Hide file tree
Showing 16 changed files with 174 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
...require('./index.json')
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"title": "Increasing charity revenue by rethinking donations on mobile",
"slug": "increasing-charity-revenue-by-rethinking-donations-on-mobile",
"date": "2017-07-20T12:00:00.284Z",
"description": "I redesigned the end to end flow for how people give to fundraisers on Everydayhero. Focusing on mobile resulted in a huge jump in donations.",
"image": "/public/images/everydayhero-donation-form-hero.jpg",
"tags": ["Research","Facilitation","UI","UX"],
"status": "published"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
**Everydayhero was a charity crowdfunding platform** where individuals could fundraise for charities they were passionate about. Fundraisers would share their fundraising pages among friends, family, and others online to help support their campaign by donating. I was the design lead for the team working on the end to end fundraiser and donation flows.

## Mobile wasn’t performing

In 2016 we discovered (through our regular data insights team reports) that our mobile donation conversions and average donations was significantly lower on mobile. Also, mobile traffic was increasing. With more people coming from social media links.

There were some obvious potential problems with the existing long donation form. But we to validate those and explore other hypotheses, we started with user testing. Since our audience was a broad range of users that usually had no prior experience with Everydayhero, we used usertesting.com for a fast turn-around.

There were some obvious potential problems with the existing long donation form. But we to validate those and explore other hypotheses, we started with user testing. Since our audience was a broad range of users that usually had no prior experience with Everydayhero, we used usertesting.com for a fast turn-around.

![Collaborating in a design sprint workshop at Everydayhero](/public/images/everydayhero-collaborating-on-problems.jpg)
## Collaborating on the problems

Now that we had a great idea of the improvement opportunities from user testing we decided to run a Design Sprint to bring in the team and other experts across support and customer success.

We played back key insights, the rest of the team added their own context, and together we landed on our top priority opportunities.

As we ideated together we aligned on some key solutions. But one area we were divided was between a single page or stepped form. Best practices were telling us that focusing on one thing at a time on mobile was the better option. But some thought it was too simple a form for this approach. This was one of the key hypotheses we decided to test.

## Testing early and often

We went back to usertesting.com with 2 prototypes and key hypotheses to test. Through testing we discovered that the stepped flow was quicker to understand. It let people focus on one decision at a time, making them feel more comfortable to move forward.

Users were able to blow through some steps quickly, while other steps took a little longer to understand, and therefore they didn’t always make decisions we were optimising for. We used these results to iterate a few more times until we had a solution we were happy to move forward on executing.

Here I am explaining how we got to the final result.

![](/public/videos/donation-form-experience.mp4)

## Nailing the details

Through development there were still many details to work out. Since we were dealing with peoples finances, it was extra important to make them feel safe and confident that everything in the process was exceeding their expectations.

This involved working side-by-side with developers to nail interactions and with QA to ensure we covered every possible configuration of the form and how those details displayed on completion pages, fundraising pages, and receipts.

## Measuring success

We were excited to see some immediate results after releasing the new donation form. And not just on mobile (although these had the biggest bump).

- Donation completion rates were up 7%
- Donations increased by 10%
- Opt-in charity communications increased by 40%

These huge results validated our process and how we collaborated as a team. Allowing us to build on it for future initiatives.

![](/public/videos/donation-form-results.mp4)
13 changes: 12 additions & 1 deletion minimal.css
Original file line number Diff line number Diff line change
Expand Up @@ -532,10 +532,21 @@ hr + [role='heading'] {
box-shadow: inset 0 -0.4em 0 var(--color-29);
}

.link:hover {
.link-ghost {
box-shadow: inset 0 -0.4em 0 rgba(0, 0, 0, 0);
}

.link:hover, .link:focus, .link:active, .link-ghost:hover, .link-ghost:focus, .link-ghost:active{
box-shadow: inset 0 -1.3em 0 var(--color-29);
}

.link:has(> img) {
box-shadow: none !important;
}

@media screen and (min-width: 1264px) {
.inline-media {
max-width: 1200px !important;
margin: 0 -10%;
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/donation-form-results-poster.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/videos/donation-form-experience.mp4
Binary file not shown.
Binary file added public/videos/donation-form-results.mp4
Binary file not shown.
26 changes: 18 additions & 8 deletions src/app/work/[post].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import React from 'react'
import { MarkdownTheme } from '@/components/MarkdownTheme'
import { PageHeader } from '@/components/PageHeader'
import { MainContainer } from '@/components/MainContainer'
import { FormattedDate } from '@/components/FormattedDate'
import { meta, site } from '@/Data'
import { Paragraph } from '@/components/Paragraph'
import { Link } from '@/components/Link'

export async function generateStaticParams (): Promise<{ post: string }[]> {
return mdxctx
Expand All @@ -16,16 +17,15 @@ export async function generateStaticParams (): Promise<{ post: string }[]> {
}

// @ts-ignore Type added in build step
const mdxctx = require.context('../../../work', true, /\.(mdx|js)$/)
const mdxctx = require.context('../../../content/work', true, /\.(mdx|js)$/)

type PostInfo = {
title: string
description: string
date: string
slug: string
teaserImage: string
embedLink: string
slidesLink: string
tags: string[]
image: string
}

function useData (postId: string): null | {
Expand Down Expand Up @@ -113,20 +113,30 @@ export default function Page () {
<BlogHead info={info} />
<PageHeader
title={info.title}
titleScale='$8'
image={info.image}
description={info.description}
breadcrumbLinks={[
{
href: '/work',
label: 'work'
label: 'Work'
}
]}
>
<FormattedDate date={info.date} />
<Paragraph typescale='$3' color='$subtle'>
{info.tags.map((tag, i) => (
<span key={tag}>
<span>{tag}</span>
<span>{i < info.tags.length - 1 ? ' · ' : ''}</span>
</span>
))}
</Paragraph>
</PageHeader>
<MainContainer maxWidth={1200}>
<MainContainer maxWidth={1000}>
<MarkdownTheme>
<MarkdownComponent />
</MarkdownTheme>
<Paragraph>Feel free to contact me (details below) for more info on this project. You can also explore <Link href='/work'>more of my work</Link> or view my <Link href='/resume'>resume</Link>.</Paragraph>
</MainContainer>
</>
)
Expand Down
86 changes: 48 additions & 38 deletions src/app/work/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,29 @@ import React from 'react'
import { Image } from '@/components/Image'
import { PageHeader } from '@/components/PageHeader'
import { MainContainer } from '@/components/MainContainer'
import { FormattedDate } from '@/components/FormattedDate'
import { Link } from '@/components/Link'
import { Strong } from '@/components/Strong'

type PostType = {
title: string
description: string
date: string
slug: string
teaserImage: string
tags: string[]
image: string
}

type DataType = {
title: string
description: string
date: string
href: string
teaserImage: string
tags: string[]
image: string
}

// @ts-ignore Type added in build step
const mdxctx = require.context('../../../work', true, /\.(mdx|js)$/)
const mdxctx = require.context('../../../content/work', true, /\.(mdx|js)$/)

const { work } = meta

Expand All @@ -37,11 +39,12 @@ const posts = mdxctx
.map((key: any) => mdxctx(key))

const POSTS = posts
.map(({ title, date, slug, description, teaserImage }: PostType) => ({
.map(({ title, date, slug, description, tags, image }: PostType) => ({
title,
description,
date,
teaserImage: `/work/${slug}/${teaserImage}`,
tags,
image,
href: `/work/${slug}`
}))
.sort((a: { date: any }, b: { date: string }) => b.date.localeCompare(a.date))
Expand All @@ -50,56 +53,63 @@ export default function Work () {
return (
<>
<PageHeader title={work.title} description={work.description} />
<MainContainer gap='$8' maxWidth={1200}>
<MainContainer gap='$8' maxWidth={1000}>
<Paragraph>
If you're viewing this, you're seeing it while I'm currently working
on adding new pages.
<Strong>Over my 15+ years</Strong> of product design experience, I’ve
worked on countless projects, collaborating with many talented people.
Over that time I’ve grown in many different areas. I’ve chosen a few
projects that highlight some of my key strengths. Especially those
that have evolved more recently.
</Paragraph>
<Paragraph>
<Link href='mailto:[email protected]'>Get in contact</Link> if you
would like to see some work from me privately in the meantime.
I’m happy to answer any questions or go deeper on any of these
projects. Contact me at{' '}
<Link href='mailto:[email protected]'>[email protected]</Link>.
</Paragraph>
{POSTS.map((post: DataType) => {
return (
<Stack
<YStack
gap='$2'
padding='$2'
key={post.href}
borderTopColor='$thin'
borderTopWidth={1}
borderColor='$thin'
borderWidth={1}
paddingTop='$8'
borderRadius='$3'
$gtMd={{
flexDirection: 'row',
alignItems: 'center',
gap: '$4'
gap: '$4',
padding: '$4'
}}
>
<YStack
$gtMd={{
width: '40%'
}}
>
<Link key={post.href} href={post.href as Href<any>}>
<Image src={post.teaserImage} alt={`${post.title} image`} />
</Link>
</YStack>
<YStack
flexShrink={1}
gap='$2'
$gtMd={{
width: '60%'
}}
>
<FormattedDate date={post.date} />
<H2 fontFamily='$heading' typescale='$7'>
<Link key={post.href} href={post.href as Href<any>}>
<YStack flexShrink={1} gap='$2'>
<Paragraph typescale='$3' color='$subtle'>
{post.tags.map((tag, i) => (
<span key={tag}>
<span>{tag}</span>
<span>{i < post.tags.length - 1 ? ' · ' : ''}</span>
</span>
))}
</Paragraph>
<H2 typescale='$6'>
<Link
key={post.href}
href={post.href as Href<any>}
type='ghost'
>
{post.title}
</Link>
</H2>
<Paragraph>{post.description}</Paragraph>
</YStack>
</Stack>
<Link key={post.href} href={post.href as Href<any>}>
<Image src={post.image} alt={`${post.title} image`} />
</Link>
<Paragraph>{post.description}</Paragraph>
</YStack>
)
})}
<Paragraph>
More work coming soon.
</Paragraph>
</MainContainer>
</>
)
Expand Down
23 changes: 20 additions & 3 deletions src/components/Image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,42 @@ import { resolveAssetUri } from '@/utils/resolveMetroAssets'
type ImageProps = {
src: number | string
alt?: string
inline?: boolean
}

export const Image = ({ src, alt }: ImageProps) => {
export const Image = ({ src, alt, inline }: ImageProps) => {
const sourceRef = typeof src === 'string' ? { uri: src } : src
const source = resolveAssetUri(sourceRef)

if (source.uri?.match(/\.mp4$/)) {
return <video src={source.uri} controls autoPlay loop playsInline />
return (
<video
src={source.uri}
poster={source.uri
.replace('/videos', '/images')
.replace('.mp4', '-poster.jpg')}
controls
playsInline
className={inline ? 'inline-media' : ''}
style={{
borderRadius: 'var(--radius-2)',
maxWidth: '100%',
width: inline ? '120%' : '100%',
}}
/>
)
}

return (
<img
src={source.uri}
alt={alt}
loading='lazy'
className={inline ? 'inline-media' : ''}
style={{
borderRadius: 'var(--radius-2)',
alignSelf: 'center',
width: '100%',
width: inline ? '120%' : '100%',
maxWidth: source.width,
height: 'auto',
aspectRatio: `${source.width} / ${source.height}`,
Expand Down
14 changes: 10 additions & 4 deletions src/components/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,28 @@ import { styled, Text } from 'tamagui'

type Props = {
href: Href<any>
type?: 'default' | 'ghost'
children: React.ReactNode
}

const StyledLink = styled(Text, {
tag: 'a',
position: 'relative',
className: 'inherit-type link',
animation: 'standard'
className: '',
animation: 'standard',
})

export const Link = ({ href, children }: Props) => (
const typeClasses = {
default: 'link',
ghost: 'link-ghost',
}

export const Link = ({ href, children, type = 'default' }: Props) => (
<ExpoLink
href={href}
target={href.startsWith('http') ? '_blank' : undefined}
asChild
>
<StyledLink>{children}</StyledLink>
<StyledLink className={`${typeClasses[type]} inherit-type`}>{children}</StyledLink>
</ExpoLink>
)
2 changes: 1 addition & 1 deletion src/components/MarkdownTheme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export function MarkdownTheme ({ children }: { children: React.ReactNode }) {
/>
)}
a={Link}
img={Image}
img={(props) => <Image inline {...props} />}
ul={({ children }) => <UnorderedList children={children} />}
ol={({ children }) => <OrderedList children={children} />}
li={({ children }) => <ListItem children={children} />}
Expand Down
Loading

0 comments on commit 470eb48

Please sign in to comment.