feat: [frontend] Update the styles of the root page, and the application
dashboard, both from claude design tool
This commit is contained in:
parent
612c33ba93
commit
678ada3031
3 changed files with 748 additions and 133 deletions
|
|
@ -1,7 +1,428 @@
|
||||||
<h1>Language Learning App</h1>
|
<script lang="ts">
|
||||||
|
import type { PageProps } from './$types';
|
||||||
|
|
||||||
<p>This is a language learning application.</p>
|
const { data }: PageProps = $props();
|
||||||
|
|
||||||
<p>
|
const now = new Date();
|
||||||
You probably want to <a href="/login">Login</a> to get started.
|
const verticalDate = now.toLocaleDateString('en-US', {
|
||||||
|
weekday: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
year: 'numeric'
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Top nav -->
|
||||||
|
<header class="sitenav">
|
||||||
|
<div class="sitenav-inner">
|
||||||
|
<a href="/" class="wordmark">Language Learning App</a>
|
||||||
|
<nav>
|
||||||
|
{#if data.isLoggedIn}
|
||||||
|
<a href="/app" class="btn btn-primary btn-sm">Go to app</a>
|
||||||
|
{:else}
|
||||||
|
<div class="auth-links">
|
||||||
|
<a href="/login" class="nav-link">Log in</a>
|
||||||
|
<a href="/register" class="btn btn-primary btn-sm">Create account</a>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Page body -->
|
||||||
|
<div class="page">
|
||||||
|
<!-- Left margin -->
|
||||||
|
<aside class="left-margin">
|
||||||
|
<div class="margin-copy">
|
||||||
|
<span class="meta-label">French · A2 → B1</span>
|
||||||
|
<span class="margin-sub">Read. Save. Review.</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="divider" />
|
||||||
|
|
||||||
|
<ul class="feature-list" role="list">
|
||||||
|
<li class="feature-item">
|
||||||
|
<span class="feature-mark">·</span>
|
||||||
|
<span>Articles & reading</span>
|
||||||
|
</li>
|
||||||
|
<li class="feature-item">
|
||||||
|
<span class="feature-mark">·</span>
|
||||||
|
<span>Vocabulary lists</span>
|
||||||
|
</li>
|
||||||
|
<li class="feature-item">
|
||||||
|
<span class="feature-mark">·</span>
|
||||||
|
<span>Spaced repetition</span>
|
||||||
|
</li>
|
||||||
|
<li class="feature-item">
|
||||||
|
<span class="feature-mark">·</span>
|
||||||
|
<span>Word packs</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- Center body -->
|
||||||
|
<main class="body">
|
||||||
|
<p class="eyebrow">A language learning application</p>
|
||||||
|
|
||||||
|
<h1 class="headline">
|
||||||
|
Read French.<br /><em>Learn naturally.</em>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p class="description">
|
||||||
|
Immerse yourself in curated and AI-generated French articles. Tap any word for a definition,
|
||||||
|
save vocabulary as you read, and review with spaced repetition — no gamification, just the
|
||||||
|
language.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
{#if data.isLoggedIn}
|
||||||
|
<a href="/app" class="btn btn-primary">
|
||||||
|
Go to app <span class="arr">→</span>
|
||||||
|
</a>
|
||||||
|
{:else}
|
||||||
|
<a href="/register" class="btn btn-primary">
|
||||||
|
Get started <span class="arr">→</span>
|
||||||
|
</a>
|
||||||
|
<a href="/login" class="btn btn-secondary">Log in</a>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="divider secondary" />
|
||||||
|
|
||||||
|
<div class="pillars">
|
||||||
|
<div class="pillar">
|
||||||
|
<span class="pillar-kicker meta-label">Exposure</span>
|
||||||
|
<p class="pillar-body">
|
||||||
|
Read articles written for your level. Bespoke content generated from topics you choose, or
|
||||||
|
browse an evergreen library.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="pillar">
|
||||||
|
<span class="pillar-kicker meta-label">Vocabulary</span>
|
||||||
|
<p class="pillar-body">
|
||||||
|
Save words as you encounter them. Import packs around themes — cuisine, travel, culture.
|
||||||
|
Build a deck that reflects your reading.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="pillar">
|
||||||
|
<span class="pillar-kicker meta-label">Review</span>
|
||||||
|
<p class="pillar-body">
|
||||||
|
Typed-recall flashcards powered by spaced repetition. No multiple choice, no confetti —
|
||||||
|
just the words, when they're due.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Right rail -->
|
||||||
|
<aside class="right-rail" aria-hidden="true">
|
||||||
|
<span class="vertical-date">{verticalDate}</span>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* ---------- Site nav ---------- */
|
||||||
|
|
||||||
|
.sitenav {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background-color: var(--glass-bg);
|
||||||
|
backdrop-filter: blur(var(--glass-blur));
|
||||||
|
-webkit-backdrop-filter: blur(var(--glass-blur));
|
||||||
|
box-shadow: 0 1px 0 color-mix(in srgb, var(--color-outline-variant) 35%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sitenav-inner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: var(--space-6);
|
||||||
|
max-width: 82rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 var(--space-6);
|
||||||
|
height: 3.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wordmark {
|
||||||
|
font-family: var(--font-display);
|
||||||
|
font-size: var(--text-body-md);
|
||||||
|
font-weight: var(--weight-semibold);
|
||||||
|
letter-spacing: var(--tracking-wide);
|
||||||
|
color: var(--color-on-surface);
|
||||||
|
text-decoration: none;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-links {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link {
|
||||||
|
font-family: var(--font-label);
|
||||||
|
font-size: var(--text-label-lg);
|
||||||
|
font-weight: var(--weight-medium);
|
||||||
|
letter-spacing: var(--tracking-wide);
|
||||||
|
color: var(--color-on-surface-variant);
|
||||||
|
text-decoration: none;
|
||||||
|
padding: var(--space-1) var(--space-3);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
transition: color var(--duration-fast) var(--ease-standard);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link:hover {
|
||||||
|
color: var(--color-on-surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Layout ---------- */
|
||||||
|
|
||||||
|
.page {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 260px 1fr 100px;
|
||||||
|
min-height: calc(100vh - 3.25rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Left margin ---------- */
|
||||||
|
|
||||||
|
.left-margin {
|
||||||
|
border-right: 1px solid var(--color-outline-variant);
|
||||||
|
padding: var(--space-12) var(--space-5) var(--space-8);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-copy {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-sub {
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: var(--text-headline-sm);
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--color-primary);
|
||||||
|
line-height: var(--leading-snug);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-list {
|
||||||
|
list-style: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0;
|
||||||
|
margin-top: var(--space-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-item {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--space-2);
|
||||||
|
font-family: var(--font-label);
|
||||||
|
font-size: var(--text-label-md);
|
||||||
|
font-weight: var(--weight-medium);
|
||||||
|
letter-spacing: var(--tracking-wide);
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-on-surface-variant);
|
||||||
|
line-height: var(--leading-loose);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-mark {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Center body ---------- */
|
||||||
|
|
||||||
|
.body {
|
||||||
|
padding: var(--space-12) var(--space-12) var(--space-10);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-5);
|
||||||
|
max-width: 52rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-label);
|
||||||
|
font-size: var(--text-label-md);
|
||||||
|
font-weight: var(--weight-medium);
|
||||||
|
letter-spacing: var(--tracking-wide);
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-on-surface-variant);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headline {
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: clamp(3rem, 6vw, 5rem);
|
||||||
|
font-weight: var(--weight-regular);
|
||||||
|
line-height: var(--leading-tight);
|
||||||
|
letter-spacing: var(--tracking-tight);
|
||||||
|
color: var(--color-on-surface);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headline em {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: var(--text-body-xl);
|
||||||
|
line-height: var(--leading-relaxed);
|
||||||
|
color: var(--color-on-surface-variant);
|
||||||
|
max-width: 38rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-3);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arr {
|
||||||
|
font-family: var(--font-body);
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid var(--color-outline-variant);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider.secondary {
|
||||||
|
margin-top: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Pillars ---------- */
|
||||||
|
|
||||||
|
.pillars {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: var(--space-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pillar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pillar-kicker {
|
||||||
|
color: var(--color-on-surface-variant);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pillar-body {
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: var(--text-body-md);
|
||||||
|
line-height: var(--leading-relaxed);
|
||||||
|
color: var(--color-on-surface-variant);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Right rail ---------- */
|
||||||
|
|
||||||
|
.right-rail {
|
||||||
|
border-left: 1px dashed var(--color-outline-variant);
|
||||||
|
padding: var(--space-12) var(--space-4);
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-date {
|
||||||
|
font-family: var(--font-label);
|
||||||
|
font-size: var(--text-label-sm);
|
||||||
|
font-weight: var(--weight-medium);
|
||||||
|
letter-spacing: var(--tracking-wider);
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-outline);
|
||||||
|
writing-mode: vertical-rl;
|
||||||
|
transform: rotate(180deg);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Shared utility ---------- */
|
||||||
|
|
||||||
|
.meta-label {
|
||||||
|
font-family: var(--font-label);
|
||||||
|
font-size: var(--text-label-md);
|
||||||
|
font-weight: var(--weight-medium);
|
||||||
|
letter-spacing: var(--tracking-wide);
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-on-surface-variant);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Responsive ---------- */
|
||||||
|
|
||||||
|
@media (max-width: 960px) {
|
||||||
|
.page {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-margin {
|
||||||
|
border-right: none;
|
||||||
|
border-bottom: 1px solid var(--color-outline-variant);
|
||||||
|
padding: var(--space-6) var(--space-6) var(--space-5);
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: var(--space-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider:first-of-type {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-list {
|
||||||
|
flex-direction: row;
|
||||||
|
gap: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-rail {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
padding: var(--space-8) var(--space-6) var(--space-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pillars {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: var(--space-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.sitenav-inner {
|
||||||
|
padding: 0 var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wordmark {
|
||||||
|
font-size: var(--text-label-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
padding: var(--space-6) var(--space-4) var(--space-8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.headline {
|
||||||
|
font-size: var(--text-display-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
font-size: var(--text-body-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-list {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,172 +1,348 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
const hour = new Date().getHours();
|
const now = new Date();
|
||||||
const greeting = hour < 12 ? 'Good morning' : hour < 17 ? 'Good afternoon' : 'Good evening';
|
const dayName = now.toLocaleDateString('en-US', { weekday: 'long' });
|
||||||
|
const dateShort = now.toLocaleDateString('en-US', { day: 'numeric', month: 'long' });
|
||||||
|
const dateFull = now.toLocaleDateString('en-US', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
year: 'numeric'
|
||||||
|
});
|
||||||
|
const verticalDate = `${dayName} · ${dateFull}`;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<div class="hero">
|
<!-- Left margin -->
|
||||||
<p class="eyebrow label-md">Dashboard</p>
|
<aside class="left-margin">
|
||||||
<h1 class="hero-heading">{greeting}.</h1>
|
<div class="margin-date">
|
||||||
<p class="hero-sub">What will you learn today?</p>
|
<span class="meta-label">{dayName}</span>
|
||||||
|
<span class="margin-day">{dateShort}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-grid">
|
<hr class="divider" />
|
||||||
<a href="/app/articles" class="card card--primary">
|
|
||||||
<div class="card-kicker label-md">Read</div>
|
|
||||||
<h2 class="card-title">Articles</h2>
|
|
||||||
<p class="card-body">Browse your reading library and practice with word-by-word translations.</p>
|
|
||||||
<span class="card-cta" aria-hidden="true">Open library →</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="/app/generate/summary" class="card">
|
<span class="meta-label">Today</span>
|
||||||
<div class="card-kicker label-md">Create</div>
|
<ul class="today-list" role="list">
|
||||||
<h2 class="card-title">New article</h2>
|
<li><a href="/app/articles" class="today-item">· Read</a></li>
|
||||||
<p class="card-body">Generate a new reading from any text in the language you're learning.</p>
|
<li><a href="/app/generate/summary" class="today-item">· Create</a></li>
|
||||||
<span class="card-cta" aria-hidden="true">Get started →</span>
|
<li><a href="/app/packs" class="today-item">· Packs</a></li>
|
||||||
</a>
|
</ul>
|
||||||
|
</aside>
|
||||||
|
|
||||||
<a href="/app/jobs" class="card">
|
<!-- Center body -->
|
||||||
<div class="card-kicker label-md">History</div>
|
<main class="body">
|
||||||
<h2 class="card-title">Jobs</h2>
|
<p class="eyebrow">Your reading library</p>
|
||||||
<p class="card-body">Review the status of your generation jobs and access completed content.</p>
|
|
||||||
<span class="card-cta" aria-hidden="true">View jobs →</span>
|
<h1 class="headline">
|
||||||
|
Articles & <em>reading</em>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p class="description">
|
||||||
|
Browse your library of French articles and generated readings. Tap any word for a definition
|
||||||
|
and save vocabulary as you go.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<a href="/app/articles" class="btn btn-primary">
|
||||||
|
Open library <span class="arr">→</span>
|
||||||
|
</a>
|
||||||
|
<span class="meta-label">or generate a new article below</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="divider secondary" />
|
||||||
|
|
||||||
|
<div class="secondary-items">
|
||||||
|
<a href="/app/generate/summary" class="secondary-item">
|
||||||
|
<span class="secondary-kicker meta-label">Create</span>
|
||||||
|
<span class="secondary-title">New article</span>
|
||||||
|
<span class="secondary-arrow">→</span>
|
||||||
|
</a>
|
||||||
|
<a href="/app/packs" class="secondary-item">
|
||||||
|
<span class="secondary-kicker meta-label">Browse</span>
|
||||||
|
<span class="secondary-title">Word packs</span>
|
||||||
|
<span class="secondary-arrow">→</span>
|
||||||
|
</a>
|
||||||
|
<a href="/app/jobs" class="secondary-item">
|
||||||
|
<span class="secondary-kicker meta-label">History</span>
|
||||||
|
<span class="secondary-title">Jobs</span>
|
||||||
|
<span class="secondary-arrow">→</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Right rail -->
|
||||||
|
<aside class="right-rail" aria-hidden="true">
|
||||||
|
<span class="vertical-date">{verticalDate}</span>
|
||||||
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
/* ---------- Layout ---------- */
|
||||||
|
|
||||||
.page {
|
.page {
|
||||||
max-width: 60rem;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: var(--space-12) var(--space-6) var(--space-8);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-10);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Hero --- */
|
|
||||||
|
|
||||||
.hero {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.eyebrow {
|
|
||||||
color: var(--color-on-surface-variant);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-heading {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: var(--text-display-md);
|
|
||||||
font-weight: var(--weight-bold);
|
|
||||||
line-height: var(--leading-tight);
|
|
||||||
letter-spacing: var(--tracking-tight);
|
|
||||||
color: var(--color-on-surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-sub {
|
|
||||||
font-family: var(--font-body);
|
|
||||||
font-size: var(--text-body-lg);
|
|
||||||
color: var(--color-on-surface-variant);
|
|
||||||
margin-top: var(--space-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Card grid --- */
|
|
||||||
|
|
||||||
.card-grid {
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
|
grid-template-columns: 260px 1fr 100px;
|
||||||
|
min-height: calc(100vh - 3.25rem); /* below TopNav */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Left margin ---------- */
|
||||||
|
|
||||||
|
.left-margin {
|
||||||
|
border-right: 1px solid var(--color-outline-variant);
|
||||||
|
padding: var(--space-12) var(--space-5) var(--space-8);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
gap: var(--space-4);
|
gap: var(--space-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Card --- */
|
.margin-date {
|
||||||
|
|
||||||
.card {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--space-2);
|
gap: var(--space-1);
|
||||||
padding: var(--space-6);
|
}
|
||||||
background-color: var(--color-surface-container-low);
|
|
||||||
border-radius: var(--radius-xl);
|
.margin-day {
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: var(--text-headline-sm);
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--color-primary);
|
||||||
|
line-height: var(--leading-snug);
|
||||||
|
}
|
||||||
|
|
||||||
|
.today-list {
|
||||||
|
list-style: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0;
|
||||||
|
margin-top: var(--space-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.today-item {
|
||||||
|
font-family: var(--font-label);
|
||||||
|
font-size: var(--text-label-md);
|
||||||
|
font-weight: var(--weight-medium);
|
||||||
|
letter-spacing: var(--tracking-wide);
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-on-surface-variant);
|
||||||
|
text-decoration: none;
|
||||||
|
line-height: var(--leading-loose);
|
||||||
|
transition: color var(--duration-fast) var(--ease-standard);
|
||||||
|
}
|
||||||
|
|
||||||
|
.today-item:hover {
|
||||||
|
color: var(--color-on-surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Center body ---------- */
|
||||||
|
|
||||||
|
.body {
|
||||||
|
padding: var(--space-12) var(--space-12) var(--space-10);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-5);
|
||||||
|
max-width: 48rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-label);
|
||||||
|
font-size: var(--text-label-md);
|
||||||
|
font-weight: var(--weight-medium);
|
||||||
|
letter-spacing: var(--tracking-wide);
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-on-surface-variant);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headline {
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: clamp(3rem, 6vw, 5rem);
|
||||||
|
font-weight: var(--weight-regular);
|
||||||
|
line-height: var(--leading-tight);
|
||||||
|
letter-spacing: var(--tracking-tight);
|
||||||
|
color: var(--color-on-surface);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headline em {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: var(--text-body-xl);
|
||||||
|
line-height: var(--leading-relaxed);
|
||||||
|
color: var(--color-on-surface-variant);
|
||||||
|
max-width: 36rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-4);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arr {
|
||||||
|
font-family: var(--font-body);
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid var(--color-outline-variant);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider.secondary {
|
||||||
|
margin-top: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Secondary items ---------- */
|
||||||
|
|
||||||
|
.secondary-items {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary-item {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 5rem 1fr auto;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: var(--space-4);
|
||||||
|
padding: var(--space-3) 0;
|
||||||
|
border-bottom: 1px solid var(--color-surface-container);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
transition: background-color var(--duration-fast) var(--ease-standard);
|
transition: background-color var(--duration-fast) var(--ease-standard);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card:hover {
|
.secondary-item:last-child {
|
||||||
background-color: var(--color-surface-container);
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card--primary {
|
.secondary-kicker {
|
||||||
background-color: var(--color-primary-container);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card--primary:hover {
|
|
||||||
background-color: color-mix(in srgb, var(--color-primary-container) 80%, var(--color-primary));
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-kicker {
|
|
||||||
color: var(--color-on-surface-variant);
|
color: var(--color-on-surface-variant);
|
||||||
margin-bottom: var(--space-1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card--primary .card-kicker {
|
.secondary-title {
|
||||||
color: var(--color-on-primary-container);
|
font-family: var(--font-body);
|
||||||
opacity: 0.75;
|
font-size: var(--text-title-lg);
|
||||||
}
|
font-style: italic;
|
||||||
|
|
||||||
.card-title {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: var(--text-headline-md);
|
|
||||||
font-weight: var(--weight-semibold);
|
|
||||||
line-height: var(--leading-snug);
|
|
||||||
color: var(--color-on-surface);
|
color: var(--color-on-surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card--primary .card-title {
|
.secondary-arrow {
|
||||||
color: var(--color-on-primary-container);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-body {
|
|
||||||
font-family: var(--font-body);
|
font-family: var(--font-body);
|
||||||
font-size: var(--text-body-sm);
|
font-size: var(--text-body-md);
|
||||||
color: var(--color-on-surface-variant);
|
color: var(--color-on-surface-variant);
|
||||||
line-height: var(--leading-relaxed);
|
opacity: 0;
|
||||||
flex: 1;
|
transition: opacity var(--duration-fast) var(--ease-standard);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card--primary .card-body {
|
.secondary-item:hover .secondary-arrow {
|
||||||
color: var(--color-on-primary-container);
|
opacity: 1;
|
||||||
opacity: 0.8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-cta {
|
.secondary-item:hover .secondary-title {
|
||||||
font-family: var(--font-label);
|
|
||||||
font-size: var(--text-label-lg);
|
|
||||||
font-weight: var(--weight-medium);
|
|
||||||
color: var(--color-primary);
|
color: var(--color-primary);
|
||||||
margin-top: var(--space-2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card--primary .card-cta {
|
/* ---------- Right rail ---------- */
|
||||||
color: var(--color-on-primary-container);
|
|
||||||
|
.right-rail {
|
||||||
|
border-left: 1px dashed var(--color-outline-variant);
|
||||||
|
padding: var(--space-12) var(--space-4);
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Responsive --- */
|
.vertical-date {
|
||||||
|
font-family: var(--font-label);
|
||||||
|
font-size: var(--text-label-sm);
|
||||||
|
font-weight: var(--weight-medium);
|
||||||
|
letter-spacing: var(--tracking-wider);
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-outline);
|
||||||
|
writing-mode: vertical-rl;
|
||||||
|
transform: rotate(180deg);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Shared utility ---------- */
|
||||||
|
|
||||||
|
.meta-label {
|
||||||
|
font-family: var(--font-label);
|
||||||
|
font-size: var(--text-label-md);
|
||||||
|
font-weight: var(--weight-medium);
|
||||||
|
letter-spacing: var(--tracking-wide);
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-on-surface-variant);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Responsive ---------- */
|
||||||
|
|
||||||
|
@media (max-width: 960px) {
|
||||||
|
.page {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-margin {
|
||||||
|
border-right: none;
|
||||||
|
border-bottom: 1px solid var(--color-outline-variant);
|
||||||
|
padding: var(--space-6) var(--space-6) var(--space-5);
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: var(--space-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-date {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-day {
|
||||||
|
font-size: var(--text-body-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider:first-of-type {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.today-list {
|
||||||
|
flex-direction: row;
|
||||||
|
gap: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-rail {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
padding: var(--space-8) var(--space-6) var(--space-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
.headline {
|
||||||
|
font-size: var(--text-display-md);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
.page {
|
.body {
|
||||||
padding: var(--space-8) var(--space-4) var(--space-6);
|
padding: var(--space-6) var(--space-4) var(--space-8);
|
||||||
gap: var(--space-8);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-heading {
|
.headline {
|
||||||
font-size: var(--text-headline-lg);
|
font-size: var(--text-display-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-grid {
|
.description {
|
||||||
grid-template-columns: 1fr;
|
font-size: var(--text-body-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
import { query, getRequestEvent } from '$app/server';
|
import { query, getRequestEvent } from '$app/server';
|
||||||
import * as v from 'valibot';
|
import * as v from 'valibot';
|
||||||
import { searchWordformsApiDictionaryWordformsGet } from '../../../../client';
|
import {
|
||||||
|
searchWordformsApiDictionarySearchGet,
|
||||||
|
searchWordformsApiDictionaryWordformsGet
|
||||||
|
} from '../../../../client';
|
||||||
import { COOKIE_NAME_AUTH_TOKEN } from '$lib/auth';
|
import { COOKIE_NAME_AUTH_TOKEN } from '$lib/auth';
|
||||||
|
|
||||||
export const dictionarySearch = query(
|
export const dictionarySearch = query(
|
||||||
|
|
@ -10,11 +13,26 @@ export const dictionarySearch = query(
|
||||||
}),
|
}),
|
||||||
async ({ langCode, text }) => {
|
async ({ langCode, text }) => {
|
||||||
const { cookies } = getRequestEvent();
|
const { cookies } = getRequestEvent();
|
||||||
|
const trimmed = text.trim();
|
||||||
|
|
||||||
|
if (trimmed.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trimmed.length < 5) {
|
||||||
const { data } = await searchWordformsApiDictionaryWordformsGet({
|
const { data } = await searchWordformsApiDictionaryWordformsGet({
|
||||||
headers: { Authorization: `Bearer ${cookies.get(COOKIE_NAME_AUTH_TOKEN)}` },
|
headers: { Authorization: `Bearer ${cookies.get(COOKIE_NAME_AUTH_TOKEN)}` },
|
||||||
query: { lang_code: langCode, text }
|
query: { lang_code: langCode, text }
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
} else {
|
||||||
|
const { data } = await searchWordformsApiDictionarySearchGet({
|
||||||
|
headers: { Authorization: `Bearer ${cookies.get(COOKIE_NAME_AUTH_TOKEN)}` },
|
||||||
|
query: { lang_code: langCode, text }
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue