Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 157 additions & 20 deletions src/lib/components/organisms/ChatCard.svelte
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
<script lang="ts">
import { reveal } from '$lib/utils/reveal';

// Twelve bars, tallest in the middle: a voice note that looks like speech.
const bars = [3, 6, 10, 14, 9, 16, 12, 18, 8, 13, 6, 4];
</script>

<!--
The whole product in one card, after the three features that make it up:
who you are matched with, two messages, a correction, and translation one
tap away.
The whole product in two cards, after the three features that make it up.
Left: who you are matched with, two messages, a correction, and translation
one tap away. Right: a translated message, a voice note, a photo, and the
streak and tokens that come from showing up.
-->
<section class="wrap">
<div
class="card"
role="img"
aria-label="A LangX chat: two messages, then a correction"
data-reveal
use:reveal={{ y: 40 }}
>
<section class="wrap" data-reveal-children use:reveal={{ children: true, stagger: 0.12, y: 40 }}>
<div class="card" role="img" aria-label="A LangX chat: two messages, then a correction">
<div class="who">
<span class="avatar" aria-hidden="true">M</span>
<span class="meta">
Expand All @@ -24,7 +22,7 @@
</div>
<p class="bubble theirs">¡Hola! Yo aprendo inglés hace dos años 😄</p>
<p class="bubble mine">Estudio español hace dos mes</p>
<div class="correction">
<div class="note correction">
<span class="label">Correction</span>
<span class="text">hace dos <s>mes</s> <strong>meses</strong> ✓</span>
</div>
Expand All @@ -46,15 +44,57 @@
Translate
</span>
</div>

<div
class="card"
role="img"
aria-label="A LangX chat: a translated message, a voice note, a photo, and a streak"
>
<div class="who">
<span class="avatar green" aria-hidden="true">K</span>
<span class="meta">
<span class="name">Kenji</span>
<span class="langs">speaks Japanese · learning English</span>
</span>
</div>
<p class="bubble theirs">6時に駅で会おう!</p>
<div class="note translation">
<span class="label">Translation</span>
<span class="text">Let's meet at the station at 6!</span>
</div>
<div class="bubble mine voice">
<span class="play" aria-hidden="true">
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"
><path d="M7 4v16l13-8z" /></svg
>
</span>
<span class="wave" aria-hidden="true">
{#each bars as h}<i style="height:{h}px" />{/each}
</span>
<span class="length">0:07</span>
</div>
<div class="bubble theirs photo">
<span class="picture" aria-hidden="true">☕</span>
<span class="caption">駅前のカフェ</span>
</div>
<div class="earned">
<span>🔥 12-day streak</span>
<span>+20 tokens today</span>
</div>
</div>
</section>

<style lang="scss">
@import '$lib/scss/breakpoints.scss';

.wrap {
max-width: 400px;
max-width: 840px;
margin: 0 auto;
padding: 110px 0 0;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 24px;
align-items: start;

@include for-phone-only {
padding-top: 72px;
Expand Down Expand Up @@ -91,6 +131,11 @@
font-weight: 800;
font-size: 0.875rem;
color: var(--color--accent-shade);

&.green {
background: var(--color--success-tint);
color: var(--color--success);
}
}

.meta {
Expand Down Expand Up @@ -131,11 +176,9 @@
border-bottom-right-radius: 4px;
}

.correction {
align-self: flex-end;
.note {
max-width: 85%;
border: 2px solid var(--color--success);
background: var(--color--success-tint);
border: 2px solid;
border-radius: var(--radius-lg);
padding: 8px 14px;
display: flex;
Expand All @@ -148,18 +191,34 @@
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
color: var(--color--success);
}

.text {
font-size: 0.875rem;
}

.correction {
align-self: flex-end;
border-color: var(--color--success);
background: var(--color--success-tint);

.label,
strong {
color: var(--color--success);
}

s {
color: var(--color--text-tertiary);
}
}

strong {
color: var(--color--success);
.translation {
align-self: flex-start;
border-color: var(--color--accent-tint);
background: var(--color--accent-tint);

.label {
color: var(--color--accent-shade);
}
}

Expand All @@ -173,4 +232,82 @@
font-weight: 600;
color: var(--color--accent);
}

// A voice note: play, the shape of the sound, how long it is.
.voice {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 14px 8px 10px;
}

.play {
width: 26px;
height: 26px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.25);
display: grid;
place-items: center;
}

.wave {
display: flex;
align-items: center;
gap: 2px;
height: 18px;

i {
display: block;
width: 3px;
border-radius: 2px;
background: currentColor;
opacity: 0.9;
}
}

.length {
font-size: 0.75rem;
font-variant-numeric: tabular-nums;
opacity: 0.85;
}

// A photo: the tile stands in for the picture, the caption is the message.
.photo {
display: flex;
flex-direction: column;
gap: 6px;
padding: 6px 6px 8px;
}

.picture {
width: 150px;
height: 96px;
border-radius: 12px;
background: linear-gradient(135deg, var(--color--accent-tint), var(--color--success-tint));
display: grid;
place-items: center;
font-size: 1.75rem;
}

.caption {
padding: 0 8px;
font-size: 0.875rem;
}

.earned {
display: flex;
flex-wrap: wrap;
gap: 8px;
padding-top: 12px;
border-top: 1px solid var(--color--border);

span {
font-size: 0.75rem;
font-weight: 700;
padding: 4px 10px;
border-radius: var(--radius-pill);
background: var(--color--muted);
color: var(--color--text-shade);
}
}
</style>
103 changes: 103 additions & 0 deletions src/lib/components/organisms/NotFound.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<script lang="ts">
import Button from '$lib/components/atoms/Button.svelte';

/** The HTTP status; anything but 404 is "something went wrong". */
export let status = 404;

$: missing = status === 404;
</script>

<div class="missing">
<div class="copy">
<span class="eyebrow">{status}</span>
<h1>{missing ? 'Nothing here, sorry.' : 'Something went wrong.'}</h1>
<p class="lede">
{missing
? 'The page moved or never existed. The homepage has everything that does.'
: 'Reload the page, or start again from the homepage.'}
</p>
<Button href="/" variant="secondary">Back to the start</Button>
</div>
{#if missing}
<!-- The one chat that fits a missing page: a question, a slip, a correction. -->
<div class="chat" aria-hidden="true">
<span class="bubble theirs">¿Dónde está la página?</span>
<span class="bubble mine">No sé, aquí no esta 🤷</span>
<span class="note"><s>esta</s> <strong>está</strong> ✓</span>
</div>
{/if}
</div>

<style lang="scss">
.missing {
min-height: 60vh;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 48px;
padding: var(--space-2xl) 0;
}

.copy {
flex: 1 1 320px;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 12px;

h1 {
margin: 0;
max-width: 16ch;
}

.lede {
margin: 0 0 12px;
}
}

.chat {
flex: 0 1 340px;
display: flex;
flex-direction: column;
gap: 10px;
}

.bubble {
max-width: 85%;
padding: 10px 14px;
border-radius: var(--radius-lg);
font-size: 0.9375rem;
line-height: 1.45;
}

.theirs {
align-self: flex-start;
background: var(--color--muted);
border-bottom-left-radius: 4px;
}

.mine {
align-self: flex-end;
background: var(--color--accent);
color: var(--color--accent-contrast);
border-bottom-right-radius: 4px;
}

.note {
align-self: flex-end;
border: 2px solid var(--color--success);
background: var(--color--success-tint);
border-radius: var(--radius-lg);
padding: 8px 14px;
font-size: 0.875rem;

s {
color: var(--color--text-tertiary);
}

strong {
color: var(--color--success);
}
}
</style>
12 changes: 11 additions & 1 deletion src/lib/components/organisms/PageHeader.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
<script lang="ts">
/** The small blue word above the title, the way the homepage sections open. */
export let eyebrow: string | undefined = undefined;
export let title: string;
export let lede: string | undefined = undefined;
</script>

<header class="page-header">
{#if eyebrow}<span class="eyebrow">{eyebrow}</span>{/if}
<h1>{title}</h1>
{#if lede}<p class="lede">{lede}</p>{/if}
</header>
Expand All @@ -14,15 +17,22 @@
.page-header {
display: flex;
flex-direction: column;
gap: var(--space-sm);
gap: 12px;
padding: var(--space-2xl) 0 var(--space-xl);

@include for-phone-only {
padding: var(--space-xl) 0 var(--space-lg);
}

h1 {
margin: 0;
max-width: 20ch;
}

.lede {
margin: 0;
font-size: 1.0625rem;
line-height: 1.65;
}
}
</style>
Loading