diff --git a/frontend/src/app.css b/frontend/src/app.css index 68eda51..4e60d71 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -117,13 +117,17 @@ --text-headline-sm: 1.25rem; --text-title-lg: 1.125rem; --text-title-md: 1rem; - --text-body-xl: 1.25rem; /* long-form reading standard */ - --text-body-lg: 1rem; - --text-body-md: 0.9375rem; - --text-body-sm: 0.875rem; + --text-body-xl: clamp(1.56rem, 1vi + 1.31rem, 2.11rem); + --text-body-lg: clamp(1.25rem, 0.61vi + 1.1rem, 1.58rem); + --text-body-md: clamp(1rem, 0.34vi + 0.91rem, 1.19rem); + --text-body-sm: clamp(0.8rem, 0.17vi + 0.76rem, 0.89rem); --text-label-lg: 0.875rem; --text-label-md: 0.75rem; /* metadata, all-caps */ --text-label-sm: 0.6875rem; + + --fs-xl: clamp(1.95rem, 1.56vi + 1.56rem, 2.81rem); + --fs-xxl: clamp(2.44rem, 2.38vi + 1.85rem, 3.75rem); + --fs-xxxl: clamp(3.05rem, 3.54vi + 2.17rem, 5rem); /* --- Typography: Weights --- */ --weight-light: 300; @@ -138,6 +142,7 @@ --leading-normal: 1.5; --leading-relaxed: 1.6; /* "Digital Paper" body text minimum */ --leading-loose: 1.8; + --leading-xloose: 2.25; /* --- Typography: Letter Spacing --- */; --tracking-tight: -0.025em; diff --git a/frontend/src/lib/i8n/index.ts b/frontend/src/lib/i8n/index.ts new file mode 100644 index 0000000..7270ef7 --- /dev/null +++ b/frontend/src/lib/i8n/index.ts @@ -0,0 +1,29 @@ +import { derived, writable, type Writable } from 'svelte/store'; + +export type Locale = 'en' | 'fr'; +export const locale: Writable = writable('en'); + +export function makeTranslate>(translations: T, locale: Locale) { + return function (key: string, vars: Record = {}): string { + // Keys can be e.g. 'cards.title', so we split by ., and have to access + // nested props + let translation = ''; + let localeText = translations[locale]; + for (const part of key.split('.')) { + translation = localeText[part]; + if (!translation) break; + localeText = localeText[part]; + } + + if (!translation) throw new Error(`no translation found for ${locale}.${key}`); + + // Replace any passed in variables in the translation string. + // Variables are denoted by {{variableName}} in the translation string. + Object.keys(vars).map((k) => { + const regex = new RegExp(`{{${k}}}`, 'g'); + translation = translation.replace(regex, vars[k]); + }); + + return translation; + }; +} diff --git a/frontend/src/routes/app/adventures/+page.svelte b/frontend/src/routes/app/adventures/+page.svelte index 7dcbdbf..0706227 100644 --- a/frontend/src/routes/app/adventures/+page.svelte +++ b/frontend/src/routes/app/adventures/+page.svelte @@ -1,32 +1,88 @@ {#if data.successMessage !== null} -
+
{data.successMessage}
{/if} -
-
+ + + + diff --git a/frontend/src/routes/app/adventures/AdventuresList.svelte b/frontend/src/routes/app/adventures/AdventuresList.svelte new file mode 100644 index 0000000..b583a87 --- /dev/null +++ b/frontend/src/routes/app/adventures/AdventuresList.svelte @@ -0,0 +1,99 @@ + + +{#if adventures.length === 0} +
+

No adventures yet

+

Create one to begin your next story.

+
+{:else} +
    + {#each adventures as adventure, index (adventure.id)} +
  1. + +
  2. + {/each} +
+{/if} + + diff --git a/frontend/src/routes/app/adventures/AdventuresListItem.svelte b/frontend/src/routes/app/adventures/AdventuresListItem.svelte new file mode 100644 index 0000000..6e64958 --- /dev/null +++ b/frontend/src/routes/app/adventures/AdventuresListItem.svelte @@ -0,0 +1,82 @@ + + + +

{eyebrowText}

+

{title}

+ {#if description} +

{description}

+ {/if} +

{dateFormatter.format(createdAt)}

+
+ + diff --git a/frontend/src/routes/app/adventures/[id]/+page.server.ts b/frontend/src/routes/app/adventures/[id]/+page.server.ts index 19bd7fc..bd8a64b 100644 --- a/frontend/src/routes/app/adventures/[id]/+page.server.ts +++ b/frontend/src/routes/app/adventures/[id]/+page.server.ts @@ -18,12 +18,13 @@ export const load: PageServerLoad = async ({ locals, params }) => { return error(400, `Error loading adventure`); } - const { title, entries, current_entry_choices } = response.data; + const { title, entries, current_entry_choices, language } = response.data; response.data.entries.forEach((e) => console.log(e.story_text)); return { title: title, entries, - choices: current_entry_choices + choices: current_entry_choices, + language: language }; }; diff --git a/frontend/src/routes/app/adventures/[id]/+page.svelte b/frontend/src/routes/app/adventures/[id]/+page.svelte index 4bd7381..d04fb6f 100644 --- a/frontend/src/routes/app/adventures/[id]/+page.svelte +++ b/frontend/src/routes/app/adventures/[id]/+page.svelte @@ -1,7 +1,9 @@
@@ -33,18 +45,16 @@ - {#if latestEntry} - ({ - label: choice.text, - id: choice.id - }))} - adventureId={params.id} - /> - {/if} + ({ + label: choice.text, + id: choice.id + }))} + adventureId={params.id} + />