refactor(components): replace all hardcoded colors with semantic tokens

Replaces bg-white, text-brand-navy, border-brand-sand, text-gray-*, bg-[#2A2A2A],
bg-brand-purple/15, hover:bg-brand-sand, etc. across all 35 .svelte files with
semantic token utilities (bg-surface, text-ink, border-line, bg-pdf-bg, bg-nav-active,
bg-muted, text-accent, bg-primary, ...).

Also adds CSS filter: invert(1) in layout.css for De Gruyter <img> icons in dark mode,
excluding icons that carry .invert already (to prevent double-inversion).

Closes #64
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-03-25 12:22:50 +01:00
parent caba89dacc
commit e4539ed0f0
32 changed files with 569 additions and 613 deletions

View File

@@ -65,54 +65,54 @@ async function backfillFileHashes() {
<div class="mx-auto max-w-7xl py-8 font-sans sm:px-6 lg:px-8">
<div class="mb-8 flex items-center justify-between">
<h1 class="font-serif text-3xl text-brand-navy">{m.admin_heading()}</h1>
<h1 class="font-serif text-3xl text-ink">{m.admin_heading()}</h1>
<!-- Tabs -->
<div class="flex rounded-lg border border-gray-200 bg-white p-1 shadow-sm">
<div class="flex rounded-lg border border-line bg-surface p-1 shadow-sm">
<button
class="rounded-md px-4 py-2 text-sm font-bold tracking-wide uppercase transition {activeTab ===
'users'
? 'bg-brand-navy text-white'
: 'text-gray-500 hover:text-brand-navy'}"
? 'bg-primary text-white'
: 'text-ink-2 hover:text-ink'}"
onclick={() => (activeTab = 'users')}>{m.admin_tab_users()}</button
>
<button
class="rounded-md px-4 py-2 text-sm font-bold tracking-wide uppercase transition {activeTab ===
'groups'
? 'bg-brand-navy text-white'
: 'text-gray-500 hover:text-brand-navy'}"
? 'bg-primary text-white'
: 'text-ink-2 hover:text-ink'}"
onclick={() => (activeTab = 'groups')}>{m.admin_tab_groups()}</button
>
<button
class="rounded-md px-4 py-2 text-sm font-bold tracking-wide uppercase transition {activeTab ===
'tags'
? 'bg-brand-navy text-white'
: 'text-gray-500 hover:text-brand-navy'}"
? 'bg-primary text-white'
: 'text-ink-2 hover:text-ink'}"
onclick={() => (activeTab = 'tags')}>{m.admin_tab_tags()}</button
>
<button
class="rounded-md px-4 py-2 text-sm font-bold tracking-wide uppercase transition {activeTab ===
'system'
? 'bg-brand-navy text-white'
: 'text-gray-500 hover:text-brand-navy'}"
? 'bg-primary text-white'
: 'text-ink-2 hover:text-ink'}"
onclick={() => (activeTab = 'system')}>{m.admin_tab_system()}</button
>
</div>
</div>
{#if form?.message}
<div class="mb-6 rounded border border-brand-mint/50 bg-brand-mint/20 p-4 text-brand-navy">
<div class="mb-6 rounded border border-accent/50 bg-accent/20 p-4 text-ink">
{form.message}
</div>
{/if}
{#if activeTab === 'users'}
<div class="border-brand-sand overflow-hidden rounded-lg border bg-white shadow-sm" in:slide>
<div class="flex items-center justify-between border-b border-gray-100 p-6">
<div class="overflow-hidden rounded-lg border border-line bg-surface shadow-sm" in:slide>
<div class="flex items-center justify-between border-b border-line-2 p-6">
<h2 class="text-lg font-bold text-gray-700">{m.admin_section_users()}</h2>
<a
href="/admin/users/new"
class="inline-flex items-center gap-1 rounded-sm bg-brand-navy px-4 py-2 font-sans text-xs font-bold tracking-widest text-white uppercase transition-opacity hover:opacity-80"
class="inline-flex items-center gap-1 rounded-sm bg-primary px-4 py-2 font-sans text-xs font-bold tracking-widest text-white uppercase transition-opacity hover:opacity-80"
>
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
@@ -129,35 +129,34 @@ async function backfillFileHashes() {
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-bold tracking-wider text-gray-500 uppercase"
<th class="px-6 py-3 text-left text-xs font-bold tracking-wider text-ink-2 uppercase"
>{m.admin_col_login()}</th
>
<th class="px-6 py-3 text-left text-xs font-bold tracking-wider text-gray-500 uppercase"
<th class="px-6 py-3 text-left text-xs font-bold tracking-wider text-ink-2 uppercase"
>{m.admin_col_full_name()}</th
>
<th class="px-6 py-3 text-left text-xs font-bold tracking-wider text-gray-500 uppercase"
<th class="px-6 py-3 text-left text-xs font-bold tracking-wider text-ink-2 uppercase"
>{m.admin_col_groups()}</th
>
<th
class="px-6 py-3 text-right text-xs font-bold tracking-wider text-gray-500 uppercase"
<th class="px-6 py-3 text-right text-xs font-bold tracking-wider text-ink-2 uppercase"
>{m.admin_col_actions()}</th
>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
<tbody class="divide-y divide-gray-200 bg-surface">
{#each data.users as user (user.id)}
<tr class="group/row hover:bg-gray-50">
<td class="px-6 py-4 text-sm font-medium whitespace-nowrap text-gray-900">
{user.username}
</td>
<td class="px-6 py-4 text-sm whitespace-nowrap text-gray-500">
<td class="px-6 py-4 text-sm whitespace-nowrap text-ink-2">
{#if user.firstName || user.lastName}
{user.firstName ?? ''} {user.lastName ?? ''}
{:else}
<span class="text-gray-300 italic"></span>
<span class="text-ink-3 italic"></span>
{/if}
</td>
<td class="px-6 py-4 text-sm text-gray-500">
<td class="px-6 py-4 text-sm text-ink-2">
<div class="flex flex-wrap gap-1">
{#if user.groups && user.groups.length > 0}
{#each user.groups as group (group.id)}
@@ -168,7 +167,7 @@ async function backfillFileHashes() {
</span>
{/each}
{:else}
<span class="text-xs text-gray-400 italic">{m.admin_no_groups()}</span>
<span class="text-xs text-ink-3 italic">{m.admin_no_groups()}</span>
{/if}
</div>
</td>
@@ -176,7 +175,7 @@ async function backfillFileHashes() {
<div class="flex items-center justify-end gap-4">
<a
href="/admin/users/{user.id}"
class="text-sm font-bold tracking-wide text-brand-mint uppercase hover:text-brand-navy"
class="text-sm font-bold tracking-wide text-accent uppercase hover:text-ink"
>
{m.btn_edit()}
</a>
@@ -196,7 +195,7 @@ async function backfillFileHashes() {
>
<input type="hidden" name="id" value={user.id} />
<button
class="p-1 text-gray-300 transition-colors hover:text-red-600"
class="p-1 text-ink-3 transition-colors hover:text-red-600"
title={m.admin_btn_delete_user_title()}
>
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -217,8 +216,8 @@ async function backfillFileHashes() {
</table>
</div>
{:else if activeTab === 'tags'}
<div class="border-brand-sand overflow-hidden rounded-lg border bg-white shadow-sm" in:slide>
<div class="border-b border-gray-100 bg-yellow-50/50 p-6">
<div class="overflow-hidden rounded-lg border border-line bg-surface shadow-sm" in:slide>
<div class="border-b border-line-2 bg-yellow-50/50 p-6">
<h2 class="text-lg font-bold text-gray-700">{m.admin_section_tags()}</h2>
<p class="mt-1 text-xs text-yellow-800">
{m.admin_tags_warning()}
@@ -244,7 +243,7 @@ async function backfillFileHashes() {
type="text"
name="name"
bind:value={editingTagName}
class="flex-1 rounded border-brand-mint px-2 py-1 text-sm ring-1 ring-brand-mint"
class="flex-1 rounded border-accent px-2 py-1 text-sm ring-1 ring-brand-mint"
/>
<button aria-label={m.btn_save()} class="text-green-600 hover:text-green-800"
><svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"
@@ -260,7 +259,7 @@ async function backfillFileHashes() {
type="button"
onclick={cancelEditTag}
aria-label={m.btn_cancel()}
class="text-gray-400 hover:text-gray-600"
class="text-ink-3 hover:text-ink-2"
><svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"
><path
stroke-linecap="round"
@@ -272,7 +271,7 @@ async function backfillFileHashes() {
>
</form>
{:else}
<span class="bg-brand-sand/30 rounded px-2 py-1 text-sm font-medium text-brand-navy">
<span class="rounded bg-muted px-2 py-1 text-sm font-medium text-ink">
{tag.name}
</span>
<div
@@ -281,7 +280,7 @@ async function backfillFileHashes() {
<button
onclick={() => startEditTag(tag)}
aria-label={m.admin_btn_edit_tag_label()}
class="p-1 text-gray-400 hover:text-brand-navy"
class="p-1 text-ink-3 hover:text-ink"
>
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"
><path
@@ -310,7 +309,7 @@ async function backfillFileHashes() {
<input type="hidden" name="id" value={tag.id} />
<button
aria-label={m.admin_btn_delete_tag_label()}
class="p-1 text-gray-400 hover:text-red-600"
class="p-1 text-ink-3 hover:text-red-600"
>
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"
><path
@@ -329,27 +328,26 @@ async function backfillFileHashes() {
</ul>
</div>
{:else if activeTab === 'groups'}
<div class="border-brand-sand overflow-hidden rounded-lg border bg-white shadow-sm" in:slide>
<div class="flex items-center justify-between border-b border-gray-100 p-6">
<div class="overflow-hidden rounded-lg border border-line bg-surface shadow-sm" in:slide>
<div class="flex items-center justify-between border-b border-line-2 p-6">
<h2 class="text-lg font-bold text-gray-700">{m.admin_section_groups()}</h2>
</div>
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-bold tracking-wider text-gray-500 uppercase"
<th class="px-6 py-3 text-left text-xs font-bold tracking-wider text-ink-2 uppercase"
>{m.admin_col_name()}</th
>
<th class="px-6 py-3 text-left text-xs font-bold tracking-wider text-gray-500 uppercase"
<th class="px-6 py-3 text-left text-xs font-bold tracking-wider text-ink-2 uppercase"
>{m.admin_col_permissions()}</th
>
<th
class="px-6 py-3 text-right text-xs font-bold tracking-wider text-gray-500 uppercase"
<th class="px-6 py-3 text-right text-xs font-bold tracking-wider text-ink-2 uppercase"
>{m.admin_col_actions()}</th
>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
<tbody class="divide-y divide-gray-200 bg-surface">
{#each data.groups as group (group.id)}
<tr class="group/row hover:bg-gray-50">
{#if editingGroupId === group.id}
@@ -372,7 +370,7 @@ async function backfillFileHashes() {
type="text"
name="name"
value={group.name}
class="w-full rounded border-brand-mint text-sm"
class="w-full rounded border-accent text-sm"
required
/>
</div>
@@ -380,14 +378,14 @@ async function backfillFileHashes() {
<div class="flex h-full flex-1 flex-wrap items-center gap-4 pt-2">
{#each availablePermissions as perm (perm)}
<label
class="inline-flex items-center text-xs font-bold text-gray-600 uppercase"
class="inline-flex items-center text-xs font-bold text-ink-2 uppercase"
>
<input
type="checkbox"
name="permissions"
value={perm}
checked={group.permissions.includes(perm)}
class="mr-2 rounded border-gray-300 text-brand-navy focus:ring-brand-mint"
class="mr-2 rounded border-gray-300 text-ink focus:ring-accent"
/>
{perm.replace('_', ' ')}
</label>
@@ -413,7 +411,7 @@ async function backfillFileHashes() {
type="button"
onclick={cancelEditGroup}
aria-label={m.btn_cancel()}
class="p-1 text-gray-400 hover:text-red-500"
class="p-1 text-ink-3 hover:text-red-500"
>
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"
><path
@@ -429,17 +427,17 @@ async function backfillFileHashes() {
</td>
{:else}
<!-- VIEW MODE -->
<td class="px-6 py-4 text-sm font-bold whitespace-nowrap text-brand-navy">
<td class="px-6 py-4 text-sm font-bold whitespace-nowrap text-ink">
{group.name}
</td>
<td class="px-6 py-4 text-sm text-gray-500">
<td class="px-6 py-4 text-sm text-ink-2">
<div class="flex flex-wrap gap-1">
{#each group.permissions as perm (perm)}
<span
class="rounded-full px-2 py-0.5 text-[10px] font-bold uppercase
{perm === 'ADMIN'
? 'border-red-100 bg-red-50 text-red-700'
: 'border-gray-200 bg-gray-100 text-gray-600'}"
: 'border-line bg-gray-100 text-ink-2'}"
>
{perm}
</span>
@@ -450,7 +448,7 @@ async function backfillFileHashes() {
<div class="flex items-center justify-end gap-3">
<button
onclick={() => startEditGroup(group.id)}
class="text-sm font-bold tracking-wide text-brand-mint uppercase hover:text-brand-navy"
class="text-sm font-bold tracking-wide text-accent uppercase hover:text-ink"
>
{m.btn_edit()}
</button>
@@ -469,7 +467,7 @@ async function backfillFileHashes() {
>
<input type="hidden" name="id" value={group.id} />
<button
class="p-1 text-gray-300 transition-colors hover:text-red-600"
class="p-1 text-ink-3 transition-colors hover:text-red-600"
title={m.btn_delete()}
>
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -491,8 +489,8 @@ async function backfillFileHashes() {
</table>
<!-- CREATE GROUP FORM -->
<div class="border-t border-gray-200 bg-gray-50 p-6">
<h3 class="mb-4 text-xs font-bold tracking-wide text-gray-500 uppercase">
<div class="border-t border-line bg-gray-50 p-6">
<h3 class="mb-4 text-xs font-bold tracking-wide text-ink-2 uppercase">
{m.admin_section_new_group()}
</h3>
<form
@@ -513,12 +511,12 @@ async function backfillFileHashes() {
<div class="flex items-center gap-4">
{#each availablePermissions as perm (perm)}
<label class="inline-flex items-center text-xs font-bold text-gray-600 uppercase">
<label class="inline-flex items-center text-xs font-bold text-ink-2 uppercase">
<input
type="checkbox"
name="permissions"
value={perm}
class="mr-2 rounded border-gray-300 text-brand-navy focus:ring-brand-mint"
class="mr-2 rounded border-gray-300 text-ink focus:ring-accent"
/>
{perm.replace('_', ' ')}
</label>
@@ -527,7 +525,7 @@ async function backfillFileHashes() {
<button
type="submit"
class="w-full rounded bg-brand-navy px-6 py-2 text-sm font-bold text-white uppercase hover:bg-brand-mint hover:text-brand-navy md:w-auto"
class="w-full rounded bg-primary px-6 py-2 text-sm font-bold text-white uppercase hover:bg-accent hover:text-ink md:w-auto"
>
{m.btn_create()}
</button>
@@ -535,37 +533,37 @@ async function backfillFileHashes() {
</div>
</div>
{:else if activeTab === 'system'}
<div class="border-brand-sand rounded-sm border bg-white p-6 shadow-sm">
<div class="rounded-sm border border-line bg-surface p-6 shadow-sm">
<h2 class="mb-1 text-lg font-bold text-gray-700">{m.admin_system_backfill_heading()}</h2>
<p class="mb-4 text-sm text-gray-500">{m.admin_system_backfill_description()}</p>
<p class="mb-4 text-sm text-ink-2">{m.admin_system_backfill_description()}</p>
<button
onclick={backfillVersions}
disabled={backfillLoading}
class="rounded bg-brand-navy px-6 py-2 text-sm font-bold text-white uppercase transition hover:bg-brand-mint hover:text-brand-navy disabled:cursor-not-allowed disabled:opacity-50"
class="rounded bg-primary px-6 py-2 text-sm font-bold text-white uppercase transition hover:bg-accent hover:text-ink disabled:cursor-not-allowed disabled:opacity-50"
>
{backfillLoading ? '…' : m.admin_system_backfill_btn()}
</button>
{#if backfillResult !== null}
<p class="mt-4 text-sm font-medium text-brand-navy">
<p class="mt-4 text-sm font-medium text-ink">
{m.admin_system_backfill_success({ count: backfillResult })}
</p>
{/if}
</div>
<div class="border-brand-sand mt-4 rounded-sm border bg-white p-6 shadow-sm">
<div class="mt-4 rounded-sm border border-line bg-surface p-6 shadow-sm">
<h2 class="mb-1 text-lg font-bold text-gray-700">
{m.admin_system_backfill_hashes_heading()}
</h2>
<p class="mb-4 text-sm text-gray-500">{m.admin_system_backfill_hashes_description()}</p>
<p class="mb-4 text-sm text-ink-2">{m.admin_system_backfill_hashes_description()}</p>
<button
onclick={backfillFileHashes}
disabled={backfillHashesLoading}
class="rounded bg-brand-navy px-6 py-2 text-sm font-bold text-white uppercase transition hover:bg-brand-mint hover:text-brand-navy disabled:cursor-not-allowed disabled:opacity-50"
class="rounded bg-primary px-6 py-2 text-sm font-bold text-white uppercase transition hover:bg-accent hover:text-ink disabled:cursor-not-allowed disabled:opacity-50"
>
{backfillHashesLoading ? '…' : m.admin_system_backfill_hashes_btn()}
</button>
{#if backfillHashesResult !== null}
<p class="mt-4 text-sm font-medium text-brand-navy">
<p class="mt-4 text-sm font-medium text-ink">
{m.admin_system_backfill_hashes_success({ count: backfillHashesResult })}
</p>
{/if}

View File

@@ -41,7 +41,7 @@ function handleBirthDateInput(e: Event) {
<div class="mx-auto max-w-3xl px-4 py-8 sm:px-6 lg:px-8">
<a
href="/admin"
class="group mb-4 inline-flex items-center text-xs font-bold tracking-widest text-gray-500 uppercase transition-colors hover:text-brand-navy"
class="group mb-4 inline-flex items-center text-xs font-bold tracking-widest text-ink-2 uppercase transition-colors hover:text-ink"
>
<svg
class="mr-2 h-4 w-4 transform transition-transform group-hover:-translate-x-1"
@@ -55,7 +55,7 @@ function handleBirthDateInput(e: Event) {
{m.btn_back_to_overview()}
</a>
<h1 class="mb-6 font-serif text-3xl font-bold text-brand-navy">
<h1 class="mb-6 font-serif text-3xl font-bold text-ink">
{m.admin_user_edit_heading({ username: data.editUser.username })}
</h1>
@@ -72,8 +72,8 @@ function handleBirthDateInput(e: Event) {
<form method="POST" use:enhance class="space-y-6">
<!-- Profile card -->
<div class="border-brand-sand rounded-sm border bg-white p-6 shadow-sm">
<h2 class="mb-5 text-xs font-bold tracking-widest text-gray-400 uppercase">
<div class="rounded-sm border border-line bg-surface p-6 shadow-sm">
<h2 class="mb-5 text-xs font-bold tracking-widest text-ink-3 uppercase">
{m.profile_section_personal()}
</h2>
@@ -81,7 +81,7 @@ function handleBirthDateInput(e: Event) {
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<label class="block">
<span
class="mb-1 block font-sans text-xs font-bold tracking-widest text-gray-400 uppercase"
class="mb-1 block font-sans text-xs font-bold tracking-widest text-ink-3 uppercase"
>
{m.profile_label_first_name()}
</span>
@@ -89,13 +89,13 @@ function handleBirthDateInput(e: Event) {
type="text"
name="firstName"
value={data.editUser.firstName ?? ''}
class="border-brand-sand w-full rounded-sm border px-3 py-2 font-serif text-sm focus:border-brand-navy focus:outline-none"
class="w-full rounded-sm border border-line px-3 py-2 font-serif text-sm focus:border-ink focus:outline-none"
/>
</label>
<label class="block">
<span
class="mb-1 block font-sans text-xs font-bold tracking-widest text-gray-400 uppercase"
class="mb-1 block font-sans text-xs font-bold tracking-widest text-ink-3 uppercase"
>
{m.profile_label_last_name()}
</span>
@@ -103,15 +103,13 @@ function handleBirthDateInput(e: Event) {
type="text"
name="lastName"
value={data.editUser.lastName ?? ''}
class="border-brand-sand w-full rounded-sm border px-3 py-2 font-serif text-sm focus:border-brand-navy focus:outline-none"
class="w-full rounded-sm border border-line px-3 py-2 font-serif text-sm focus:border-ink focus:outline-none"
/>
</label>
</div>
<label class="block">
<span
class="mb-1 block font-sans text-xs font-bold tracking-widest text-gray-400 uppercase"
>
<span class="mb-1 block font-sans text-xs font-bold tracking-widest text-ink-3 uppercase">
{m.profile_label_birth_date()}
</span>
<input
@@ -119,36 +117,32 @@ function handleBirthDateInput(e: Event) {
placeholder="TT.MM.JJJJ"
value={birthDateDisplay}
oninput={handleBirthDateInput}
class="border-brand-sand w-full rounded-sm border px-3 py-2 font-serif text-sm focus:border-brand-navy focus:outline-none"
class="w-full rounded-sm border border-line px-3 py-2 font-serif text-sm focus:border-ink focus:outline-none"
/>
<input type="hidden" name="birthDate" value={birthDateIso} />
</label>
<label class="block">
<span
class="mb-1 block font-sans text-xs font-bold tracking-widest text-gray-400 uppercase"
>
<span class="mb-1 block font-sans text-xs font-bold tracking-widest text-ink-3 uppercase">
{m.profile_label_email()}
</span>
<input
type="email"
name="email"
value={data.editUser.email ?? ''}
class="border-brand-sand w-full rounded-sm border px-3 py-2 font-serif text-sm focus:border-brand-navy focus:outline-none"
class="w-full rounded-sm border border-line px-3 py-2 font-serif text-sm focus:border-ink focus:outline-none"
/>
</label>
<label class="block">
<span
class="mb-1 block font-sans text-xs font-bold tracking-widest text-gray-400 uppercase"
>
<span class="mb-1 block font-sans text-xs font-bold tracking-widest text-ink-3 uppercase">
{m.profile_label_contact()}
</span>
<textarea
name="contact"
rows="3"
placeholder={m.profile_contact_placeholder()}
class="border-brand-sand w-full rounded-sm border px-3 py-2 font-serif text-sm focus:border-brand-navy focus:outline-none"
class="w-full rounded-sm border border-line px-3 py-2 font-serif text-sm focus:border-ink focus:outline-none"
>{data.editUser.contact ?? ''}</textarea
>
</label>
@@ -156,8 +150,8 @@ function handleBirthDateInput(e: Event) {
</div>
<!-- Groups card -->
<div class="border-brand-sand rounded-sm border bg-white p-6 shadow-sm">
<h2 class="mb-5 text-xs font-bold tracking-widest text-gray-400 uppercase">
<div class="rounded-sm border border-line bg-surface p-6 shadow-sm">
<h2 class="mb-5 text-xs font-bold tracking-widest text-ink-3 uppercase">
{m.admin_col_groups()}
</h2>
@@ -169,7 +163,7 @@ function handleBirthDateInput(e: Event) {
name="groupIds"
value={group.id}
checked={data.editUser.groups?.some((g: { id: string }) => g.id === group.id)}
class="rounded border-gray-300 text-brand-navy focus:ring-brand-mint"
class="rounded border-gray-300 text-ink focus:ring-accent"
/>
{group.name}
</label>
@@ -178,35 +172,31 @@ function handleBirthDateInput(e: Event) {
</div>
<!-- Password card -->
<div class="border-brand-sand rounded-sm border bg-white p-6 shadow-sm">
<h2 class="mb-5 text-xs font-bold tracking-widest text-gray-400 uppercase">
<div class="rounded-sm border border-line bg-surface p-6 shadow-sm">
<h2 class="mb-5 text-xs font-bold tracking-widest text-ink-3 uppercase">
{m.admin_label_new_password_optional()}
</h2>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<label class="block">
<span
class="mb-1 block font-sans text-xs font-bold tracking-widest text-gray-400 uppercase"
>
<span class="mb-1 block font-sans text-xs font-bold tracking-widest text-ink-3 uppercase">
{m.profile_label_new_password()}
</span>
<input
type="password"
name="newPassword"
class="border-brand-sand w-full rounded-sm border px-3 py-2 font-serif text-sm focus:border-brand-navy focus:outline-none"
class="w-full rounded-sm border border-line px-3 py-2 font-serif text-sm focus:border-ink focus:outline-none"
/>
</label>
<label class="block">
<span
class="mb-1 block font-sans text-xs font-bold tracking-widest text-gray-400 uppercase"
>
<span class="mb-1 block font-sans text-xs font-bold tracking-widest text-ink-3 uppercase">
{m.profile_label_new_password_confirm()}
</span>
<input
type="password"
name="confirmPassword"
class="border-brand-sand w-full rounded-sm border px-3 py-2 font-serif text-sm focus:border-brand-navy focus:outline-none"
class="w-full rounded-sm border border-line px-3 py-2 font-serif text-sm focus:border-ink focus:outline-none"
/>
</label>
</div>
@@ -214,17 +204,17 @@ function handleBirthDateInput(e: Event) {
<!-- Save bar -->
<div
class="border-brand-sand sticky bottom-0 z-10 -mx-4 flex items-center justify-between border-t bg-white px-6 py-4 shadow-[0_-2px_8px_rgba(0,0,0,0.06)]"
class="sticky bottom-0 z-10 -mx-4 flex items-center justify-between border-t border-line bg-surface px-6 py-4 shadow-[0_-2px_8px_rgba(0,0,0,0.06)]"
>
<a
href="/admin"
class="font-sans text-xs font-bold tracking-widest text-gray-500 uppercase hover:text-brand-navy"
class="font-sans text-xs font-bold tracking-widest text-ink-2 uppercase hover:text-ink"
>
{m.btn_cancel()}
</a>
<button
type="submit"
class="rounded-sm bg-brand-navy px-5 py-2 font-sans text-xs font-bold tracking-widest text-white uppercase transition-opacity hover:opacity-80"
class="rounded-sm bg-primary px-5 py-2 font-sans text-xs font-bold tracking-widest text-white uppercase transition-opacity hover:opacity-80"
>
{m.btn_save()}
</button>

View File

@@ -31,7 +31,7 @@ function handleBirthDateInput(e: Event) {
<div class="mx-auto max-w-3xl px-4 py-8 sm:px-6 lg:px-8">
<a
href="/admin"
class="group mb-4 inline-flex items-center text-xs font-bold tracking-widest text-gray-500 uppercase transition-colors hover:text-brand-navy"
class="group mb-4 inline-flex items-center text-xs font-bold tracking-widest text-ink-2 uppercase transition-colors hover:text-ink"
>
<svg
class="mr-2 h-4 w-4 transform transition-transform group-hover:-translate-x-1"
@@ -45,7 +45,7 @@ function handleBirthDateInput(e: Event) {
{m.btn_back_to_overview()}
</a>
<h1 class="mb-6 font-serif text-3xl font-bold text-brand-navy">{m.admin_user_new_heading()}</h1>
<h1 class="mb-6 font-serif text-3xl font-bold text-ink">{m.admin_user_new_heading()}</h1>
{#if form?.error}
<div class="mb-5 rounded border border-red-200 bg-red-50 p-3 text-sm text-red-700">
@@ -53,118 +53,104 @@ function handleBirthDateInput(e: Event) {
</div>
{/if}
<div class="border-brand-sand rounded-sm border bg-white p-6 shadow-sm">
<div class="rounded-sm border border-line bg-surface p-6 shadow-sm">
<form method="POST" use:enhance class="space-y-5">
<!-- Account -->
<h2 class="text-xs font-bold tracking-widest text-gray-400 uppercase">
<h2 class="text-xs font-bold tracking-widest text-ink-3 uppercase">
{m.admin_section_users()}
</h2>
<label class="block">
<span
class="mb-1 block font-sans text-xs font-bold tracking-widest text-gray-400 uppercase"
>
<span class="mb-1 block font-sans text-xs font-bold tracking-widest text-ink-3 uppercase">
{m.admin_col_login()}
</span>
<input
type="text"
name="username"
required
class="border-brand-sand w-full rounded-sm border px-3 py-2 font-serif text-sm focus:border-brand-navy focus:outline-none"
class="w-full rounded-sm border border-line px-3 py-2 font-serif text-sm focus:border-ink focus:outline-none"
/>
</label>
<label class="block">
<span
class="mb-1 block font-sans text-xs font-bold tracking-widest text-gray-400 uppercase"
>
<span class="mb-1 block font-sans text-xs font-bold tracking-widest text-ink-3 uppercase">
{m.admin_label_initial_password()}
</span>
<input
type="password"
name="password"
required
class="border-brand-sand w-full rounded-sm border px-3 py-2 font-serif text-sm focus:border-brand-navy focus:outline-none"
class="w-full rounded-sm border border-line px-3 py-2 font-serif text-sm focus:border-ink focus:outline-none"
/>
</label>
<!-- Profile -->
<h2 class="pt-2 text-xs font-bold tracking-widest text-gray-400 uppercase">
<h2 class="pt-2 text-xs font-bold tracking-widest text-ink-3 uppercase">
{m.profile_section_personal()}
</h2>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<label class="block">
<span
class="mb-1 block font-sans text-xs font-bold tracking-widest text-gray-400 uppercase"
>
<span class="mb-1 block font-sans text-xs font-bold tracking-widest text-ink-3 uppercase">
{m.profile_label_first_name()}
</span>
<input
type="text"
name="firstName"
class="border-brand-sand w-full rounded-sm border px-3 py-2 font-serif text-sm focus:border-brand-navy focus:outline-none"
class="w-full rounded-sm border border-line px-3 py-2 font-serif text-sm focus:border-ink focus:outline-none"
/>
</label>
<label class="block">
<span
class="mb-1 block font-sans text-xs font-bold tracking-widest text-gray-400 uppercase"
>
<span class="mb-1 block font-sans text-xs font-bold tracking-widest text-ink-3 uppercase">
{m.profile_label_last_name()}
</span>
<input
type="text"
name="lastName"
class="border-brand-sand w-full rounded-sm border px-3 py-2 font-serif text-sm focus:border-brand-navy focus:outline-none"
class="w-full rounded-sm border border-line px-3 py-2 font-serif text-sm focus:border-ink focus:outline-none"
/>
</label>
</div>
<label class="block">
<span
class="mb-1 block font-sans text-xs font-bold tracking-widest text-gray-400 uppercase"
>
<span class="mb-1 block font-sans text-xs font-bold tracking-widest text-ink-3 uppercase">
{m.profile_label_birth_date()}
</span>
<input
type="text"
placeholder="TT.MM.JJJJ"
oninput={handleBirthDateInput}
class="border-brand-sand w-full rounded-sm border px-3 py-2 font-serif text-sm focus:border-brand-navy focus:outline-none"
class="w-full rounded-sm border border-line px-3 py-2 font-serif text-sm focus:border-ink focus:outline-none"
/>
<input type="hidden" name="birthDate" value={birthDateIso} />
</label>
<label class="block">
<span
class="mb-1 block font-sans text-xs font-bold tracking-widest text-gray-400 uppercase"
>
<span class="mb-1 block font-sans text-xs font-bold tracking-widest text-ink-3 uppercase">
{m.profile_label_email()}
</span>
<input
type="email"
name="email"
class="border-brand-sand w-full rounded-sm border px-3 py-2 font-serif text-sm focus:border-brand-navy focus:outline-none"
class="w-full rounded-sm border border-line px-3 py-2 font-serif text-sm focus:border-ink focus:outline-none"
/>
</label>
<label class="block">
<span
class="mb-1 block font-sans text-xs font-bold tracking-widest text-gray-400 uppercase"
>
<span class="mb-1 block font-sans text-xs font-bold tracking-widest text-ink-3 uppercase">
{m.profile_label_contact()}
</span>
<textarea
name="contact"
rows="3"
placeholder={m.profile_contact_placeholder()}
class="border-brand-sand w-full rounded-sm border px-3 py-2 font-serif text-sm focus:border-brand-navy focus:outline-none"
class="w-full rounded-sm border border-line px-3 py-2 font-serif text-sm focus:border-ink focus:outline-none"
></textarea>
</label>
<!-- Groups -->
<h2 class="pt-2 text-xs font-bold tracking-widest text-gray-400 uppercase">
<h2 class="pt-2 text-xs font-bold tracking-widest text-ink-3 uppercase">
{m.admin_col_groups()}
</h2>
@@ -175,7 +161,7 @@ function handleBirthDateInput(e: Event) {
type="checkbox"
name="groupIds"
value={group.id}
class="rounded border-gray-300 text-brand-navy focus:ring-brand-mint"
class="rounded border-gray-300 text-ink focus:ring-accent"
/>
{group.name}
</label>
@@ -184,17 +170,17 @@ function handleBirthDateInput(e: Event) {
<!-- Save bar -->
<div
class="border-brand-sand mt-4 flex items-center justify-between rounded-sm border bg-white px-6 py-4 shadow-sm"
class="mt-4 flex items-center justify-between rounded-sm border border-line bg-surface px-6 py-4 shadow-sm"
>
<a
href="/admin"
class="font-sans text-xs font-bold tracking-widest text-gray-500 uppercase hover:text-brand-navy"
class="font-sans text-xs font-bold tracking-widest text-ink-2 uppercase hover:text-ink"
>
{m.btn_cancel()}
</a>
<button
type="submit"
class="rounded-sm bg-brand-navy px-5 py-2 font-sans text-xs font-bold tracking-widest text-white uppercase transition-opacity hover:opacity-80"
class="rounded-sm bg-primary px-5 py-2 font-sans text-xs font-bold tracking-widest text-white uppercase transition-opacity hover:opacity-80"
>
{m.btn_create()}
</button>