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
215 changes: 119 additions & 96 deletions apps/web/site/src/components/landing-page.astro

Large diffs are not rendered by default.

20 changes: 15 additions & 5 deletions apps/web/site/src/components/locale-switcher.astro
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,22 @@ type Props = {
const { locale } = Astro.props;
---

<div class="text-on-surface-variant flex items-center gap-4 font-sans text-xs font-black tracking-widest uppercase">
<a href="/en/" class:list={['hover:text-primary no-underline', locale === 'en' && 'text-on-surface font-extrabold']}
>EN</a
<div
class="flex items-center gap-4 font-sans text-xs font-black tracking-widest text-zinc-500 uppercase dark:text-zinc-400"
>
<a
href="/en/"
class:list={[
'no-underline hover:text-blue-600 dark:hover:text-blue-500',
locale === 'en' && 'font-extrabold text-zinc-950 dark:text-zinc-50',
]}>EN</a
>
<span class="opacity-30">/</span>
<a href="/es/" class:list={['hover:text-primary no-underline', locale === 'es' && 'text-on-surface font-extrabold']}
>ES</a
<a
href="/es/"
class:list={[
'no-underline hover:text-blue-600 dark:hover:text-blue-500',
locale === 'es' && 'font-extrabold text-zinc-950 dark:text-zinc-50',
]}>ES</a
>
</div>
10 changes: 5 additions & 5 deletions apps/web/site/src/components/theme-switcher.astro
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
<label
for="theme-switch-input"
tabindex="0"
class="theme-switch-label text-on-surface-variant hover:bg-surface-container-high hover:text-primary focus-visible:outline-primary dark:hover:bg-surface-container/90 relative flex h-10 w-10 cursor-pointer items-center justify-center overflow-hidden rounded-md transition-colors outline-none focus-visible:outline-2 focus-visible:outline-offset-2"
class="theme-switch-label relative flex h-10 w-10 cursor-pointer items-center justify-center overflow-hidden rounded-md text-zinc-500 transition-colors outline-none hover:bg-zinc-100 hover:text-blue-600 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-blue-500 dark:focus-visible:outline-blue-500"
>
<input id="theme-switch-input" type="checkbox" class="theme-switch-input sr-only" />
<span class="sr-only">Toggle theme</span>

<svg
class="theme-icon-light theme-icon text-primary absolute hidden h-7 w-7"
class="theme-icon-light theme-icon absolute hidden h-7 w-7 text-blue-600 dark:text-blue-500"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path
class="text-yellow-400"
class="text-amber-500 dark:text-amber-400"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
Expand All @@ -23,12 +23,12 @@
</svg>

<svg
class="theme-icon-dark theme-icon text-on-surface absolute hidden h-6 w-6"
class="theme-icon-dark theme-icon absolute hidden h-6 w-6 text-zinc-950 dark:text-zinc-50"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path
class="text-white"
class="text-zinc-50"
fill="currentColor"
stroke-width="1.5"
stroke="currentColor"
Expand Down
24 changes: 12 additions & 12 deletions apps/web/site/src/pages/docs/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,31 @@ import BaseLayout from '../../layouts/base-layout.astro';
>
<main class="mx-auto flex min-h-screen w-full max-w-5xl flex-col justify-center gap-8 px-4 py-20 sm:px-8">
<div class="space-y-4">
<p class="text-sm tracking-widest text-sky-300 uppercase">Documentation</p>
<h1 class="text-5xl font-semibold tracking-tighter text-white sm:text-6xl">
<p class="text-sm tracking-widest text-blue-600 uppercase dark:text-blue-400">Documentation</p>
<h1 class="text-5xl font-semibold tracking-tighter text-zinc-950 sm:text-6xl dark:text-zinc-50">
Themis docs live in this repository.
</h1>
<p class="max-w-2xl text-lg leading-8 text-slate-300">
<p class="max-w-2xl text-lg leading-8 text-zinc-500 dark:text-zinc-400">
Review the product, architecture, and design files directly under the repository <code>docs/</code> directory.
</p>
</div>

<section class="grid gap-6 md:grid-cols-3">
<article class="rounded-3xl border border-slate-800 bg-slate-950/60 p-6">
<h2 class="text-lg font-semibold text-white">Overview</h2>
<p class="mt-3 text-sm leading-7 text-slate-300">
<article class="rounded-3xl border border-zinc-950/10 bg-white p-6 dark:border-white/10 dark:bg-zinc-900">
<h2 class="text-lg font-semibold text-zinc-950 dark:text-zinc-50">Overview</h2>
<p class="mt-3 text-sm leading-7 text-zinc-500 dark:text-zinc-400">
Start with <code>docs/overview.md</code> for the product framing.
</p>
</article>
<article class="rounded-3xl border border-slate-800 bg-slate-950/60 p-6">
<h2 class="text-lg font-semibold text-white">Architecture</h2>
<p class="mt-3 text-sm leading-7 text-slate-300">
<article class="rounded-3xl border border-zinc-950/10 bg-white p-6 dark:border-white/10 dark:bg-zinc-900">
<h2 class="text-lg font-semibold text-zinc-950 dark:text-zinc-50">Architecture</h2>
<p class="mt-3 text-sm leading-7 text-zinc-500 dark:text-zinc-400">
Review runtime, frontend, backend, and deployment notes under <code>docs/architecture/</code>.
</p>
</article>
<article class="rounded-3xl border border-slate-800 bg-slate-950/60 p-6">
<h2 class="text-lg font-semibold text-white">Design</h2>
<p class="mt-3 text-sm leading-7 text-slate-300">
<article class="rounded-3xl border border-zinc-950/10 bg-white p-6 dark:border-white/10 dark:bg-zinc-900">
<h2 class="text-lg font-semibold text-zinc-950 dark:text-zinc-50">Design</h2>
<p class="mt-3 text-sm leading-7 text-zinc-500 dark:text-zinc-400">
Explore the visual direction, prompts, and reference assets under <code>docs/design/</code>.
</p>
</article>
Expand Down
12 changes: 6 additions & 6 deletions docs/agents/design-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ These instructions apply to Tailwind, visual design, UI polish, accessibility, a

## Core Tokens

- Display and headlines use Manrope through `--font-family-display`.
- Body text uses Inter through `--font-family`.
- Monospace text uses JetBrains Mono through `--font-family-mono`.
- Preserve the surface hierarchy: `surface`, `surface-container-low`, `surface-container`, `surface-container-high`, `surface-container-highest`.
- Display and headlines use Manrope through `--font-heading` / `font-heading`.
- Body text uses Inter through `--font-sans` / `font-sans`.
- Monospace text uses JetBrains Mono through `--font-mono` / `font-mono`.
- Preserve the raw Tailwind v4 surface ladder: light mode `bg-white` → `bg-zinc-50` → `bg-zinc-100`, dark mode `dark:bg-zinc-950` → `dark:bg-zinc-900` → `dark:bg-zinc-800`.
- Avoid solid 1px borders for sectioning; use tonal shifts.
- Use ghost borders with `outline-variant` at low opacity for inputs.
- Default card radius is `0.25rem`; small element radius is `0.125rem`.
- Use ghost borders with `border-zinc-950/10` (light) / `dark:border-white/10` (dark) for inputs and dividers.
- Default card radius is `0.75rem` (`rounded-lg`); small element radius is `0.5rem` (`rounded-md`).

## Mobile-First Layout

Expand Down
59 changes: 34 additions & 25 deletions docs/design-system/recipes.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@ All auth routes share the same `app-auth-layout` primitive. The shell renders a
<header class="mb-7 space-y-2">
<p
data-slot="kicker"
class="text-muted-fg font-mono text-xs font-semibold tracking-widest uppercase"
class="font-mono text-xs font-semibold tracking-widest text-zinc-500 uppercase dark:text-zinc-400"
i18n="@@signInKicker"
>
Account access
</p>
<h1
data-slot="title"
class="font-display text-fg text-[1.625rem] leading-tight font-bold tracking-[-0.025em]"
class="font-heading text-[1.625rem] leading-tight font-bold tracking-[-0.025em] text-zinc-950 dark:text-zinc-50"
i18n="@@signInTitle"
>
Sign in
</h1>
<p data-slot="sub" class="text-muted-fg text-[0.9375rem] leading-6" i18n="@@signInSub">
<p data-slot="sub" class="text-[0.9375rem] leading-6 text-zinc-500 dark:text-zinc-400" i18n="@@signInSub">
Welcome back. Use your work email to access your Themis workspace.
</p>
</header>
Expand Down Expand Up @@ -70,7 +70,7 @@ All auth routes share the same `app-auth-layout` primitive. The shell renders a
>
</form>

<p class="text-muted-fg mt-6 text-sm" i18n="@@signInFooterPrompt">New to Themis?</p>
<p class="mt-6 text-sm text-zinc-500 dark:text-zinc-400" i18n="@@signInFooterPrompt">New to Themis?</p>
<app-link data-slot="footer" i18n="@@signInFooterLink" [routerLink]="footerLink" [text]="'Create an account'" />
</app-auth-card>
</app-auth-layout>
Expand Down Expand Up @@ -147,7 +147,7 @@ The pin input renders a wrapper with `data-slot="pin-input"`; e2e helpers target
</app-field>
```

The `Field` propagates `data-invalid` to its children. `Input` reads the attribute to switch `border-border` to `border-danger`.
The `Field` propagates `data-invalid` to its children. `Input` reads the attribute to switch `border-zinc-950/15` to `border-red-600` (`dark:border-red-500`).

## App Shell (auth routes excluded)

Expand Down Expand Up @@ -175,17 +175,17 @@ type LayoutNavItem = {
};
```

The `Sidebar` items consume `data-current:bg-accent/10 data-current:text-accent` for the active state.
The `Sidebar` items consume `data-current:bg-blue-600/10 data-current:text-blue-600 dark:data-current:bg-blue-400/10 dark:data-current:text-blue-400` for the active state.

The user menu uses `app-dropdown` + `app-listbox` so it is keyboard accessible and the menu items are real `<div role="option">` elements that the screen reader can announce.

## Page Header

```html
<app-page-header>
<p data-slot="eyebrow" class="text-accent text-sm font-semibold">Projects</p>
<h1 data-slot="title" class="font-heading text-4xl font-bold">Project workspace</h1>
<p data-slot="description" class="text-muted-fg">Manage active work.</p>
<p data-slot="eyebrow" class="text-sm font-semibold text-blue-600 dark:text-blue-500">Projects</p>
<h1 data-slot="title" class="font-heading text-4xl font-bold text-zinc-950 dark:text-zinc-50">Project workspace</h1>
<p data-slot="description" class="text-zinc-500 dark:text-zinc-400">Manage active work.</p>
<app-button data-slot="actions" tone="blue">New project</app-button>
</app-page-header>
```
Expand All @@ -194,23 +194,30 @@ The user menu uses `app-dropdown` + `app-listbox` so it is keyboard accessible a

```html
<app-card padding="md" tone="raised" class="overflow-hidden">
<div class="bg-panel hidden grid-cols-4 gap-3 px-6 py-3 md:grid">
<span class="text-muted-fg text-xs font-semibold tracking-widest uppercase">Project</span>
<span class="text-muted-fg text-xs font-semibold tracking-widest uppercase">Status</span>
<span class="text-muted-fg text-xs font-semibold tracking-widest uppercase">Created</span>
<span class="text-muted-fg text-xs font-semibold tracking-widest uppercase">Actions</span>
<div class="hidden grid-cols-4 gap-3 bg-zinc-50 px-6 py-3 md:grid dark:bg-zinc-900">
<span class="text-xs font-semibold tracking-widest text-zinc-500 uppercase dark:text-zinc-400">Project</span>
<span class="text-xs font-semibold tracking-widest text-zinc-500 uppercase dark:text-zinc-400">Status</span>
<span class="text-xs font-semibold tracking-widest text-zinc-500 uppercase dark:text-zinc-400">Created</span>
<span class="text-xs font-semibold tracking-widest text-zinc-500 uppercase dark:text-zinc-400">Actions</span>
</div>

@for (project of projects(); track project.id) {
<article class="border-border-subtle grid gap-3 border-t px-6 py-4 md:grid-cols-4 md:items-center">
<a class="text-fg hover:text-accent font-medium no-underline" [routerLink]="['/projects', project.id]">
<article class="grid gap-3 border-t border-zinc-950/10 px-6 py-4 md:grid-cols-4 md:items-center dark:border-white/10">
<a
class="font-medium text-zinc-950 no-underline hover:text-blue-600 dark:text-zinc-50 dark:hover:text-blue-500"
[routerLink]="['/projects', project.id]"
>
{{ project.name }}
</a>
<app-badge [tone]="statusTone(project.status)">{{ statusLabel(project.status) }}</app-badge>
<span class="text-muted-fg text-xs">{{ formatDate(project.createdAt) }}</span>
<span class="text-xs text-zinc-500 dark:text-zinc-400">{{ formatDate(project.createdAt) }}</span>
<div class="flex items-center gap-2">
<a [routerLink]="['/projects', project.id]" class="text-accent text-sm font-medium">View</a>
<button type="button" class="text-danger text-sm font-medium" (click)="deleteProject(project.id, $event)">
<a [routerLink]="['/projects', project.id]" class="text-sm font-medium text-blue-600 dark:text-blue-500">View</a>
<button
type="button"
class="text-sm font-medium text-red-600 dark:text-red-400"
(click)="deleteProject(project.id, $event)"
>
Delete
</button>
</div>
Expand All @@ -226,24 +233,26 @@ The user menu uses `app-dropdown` + `app-listbox` so it is keyboard accessible a
Use the accessible `role="tablist"` / `role="tab"` / `role="tabpanel"` pattern with Tailwind utilities. No PrimeNG `TabView` is needed.

```html
<div role="tablist" class="bg-panel border-border-subtle flex flex-wrap border-b">
<div role="tablist" class="flex flex-wrap border-b border-zinc-950/10 bg-zinc-50 dark:border-white/10 dark:bg-zinc-900">
@for (tab of tabs; track tab.id) {
<button
type="button"
role="tab"
[id]="'config-tab-' + tab.id"
[attr.aria-selected]="selected() === tab.id"
[attr.aria-controls]="'config-tabpanel'"
[class.bg-bg]="selected() === tab.id"
[class.text-accent]="selected() === tab.id"
[class.bg-white]="selected() === tab.id"
[class.dark:bg-zinc-950]="selected() === tab.id"
[class.text-blue-600]="selected() === tab.id"
[class.dark:text-blue-500]="selected() === tab.id"
(click)="select(tab.id)"
>
{{ tab.label }}
</button>
}
</div>

<div role="tabpanel" [attr.aria-labelledby]="'config-tab-' + selected()" class="bg-panel-raised p-6">
<div role="tabpanel" [attr.aria-labelledby]="'config-tab-' + selected()" class="bg-zinc-100 p-6 dark:bg-zinc-800">
<pre class="font-mono text-sm leading-7 break-words whitespace-pre-wrap">{{ content() }}</pre>
</div>
```
Expand All @@ -252,8 +261,8 @@ Use the accessible `role="tablist"` / `role="tab"` / `role="tabpanel"` pattern w

```html
<app-dialog [open]="dialogOpen()" ariaLabelledBy="dialog-title" (closed)="dialogOpen.set(false)">
<h2 id="dialog-title" class="font-heading text-2xl font-bold">Confirm action</h2>
<p class="text-muted-fg">This action can be undone from settings.</p>
<h2 id="dialog-title" class="font-heading text-2xl font-bold text-zinc-950 dark:text-zinc-50">Confirm action</h2>
<p class="text-zinc-500 dark:text-zinc-400">This action can be undone from settings.</p>
</app-dialog>
```

Expand Down
Loading
Loading