- Vite + Vue 3.5 + Pinia + vue-router + Tailwind (brand accent #F5C518), dark-mode aware, deterministic package-lock.json for `npm ci`. - Session store (fetchMe/login/register/logout) over a credentials:'include' fetch client; router guards (requiresAuth / guestOnly) with lazy /me resolve. - BaseButton + BaseInput primitives (focus rings, loading, error states). - LoginView, RegisterView, and an authed BoardView shell with an empty state for the M1 masonry board — all at v1 polish (rule 24). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
36 lines
1.2 KiB
Vue
36 lines
1.2 KiB
Vue
<script setup lang="ts">
|
|
defineProps<{
|
|
id: string;
|
|
label: string;
|
|
modelValue: string;
|
|
type?: string;
|
|
autocomplete?: string;
|
|
placeholder?: string;
|
|
error?: string;
|
|
required?: boolean;
|
|
}>();
|
|
|
|
defineEmits<{ (e: "update:modelValue", value: string): void }>();
|
|
</script>
|
|
|
|
<template>
|
|
<div class="flex flex-col gap-1.5">
|
|
<label :for="id" class="text-sm font-medium text-neutral-700 dark:text-neutral-300">{{ label }}</label>
|
|
<input
|
|
:id="id"
|
|
:type="type ?? 'text'"
|
|
:value="modelValue"
|
|
:autocomplete="autocomplete"
|
|
:placeholder="placeholder"
|
|
:required="required"
|
|
:aria-invalid="error ? 'true' : undefined"
|
|
class="rounded-lg border bg-white px-3 py-2.5 text-sm text-neutral-900 shadow-sm transition
|
|
placeholder:text-neutral-400 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand
|
|
dark:bg-neutral-800 dark:text-neutral-100 dark:placeholder:text-neutral-500"
|
|
:class="error ? 'border-red-400 dark:border-red-500' : 'border-neutral-300 dark:border-neutral-700'"
|
|
@input="$emit('update:modelValue', ($event.target as HTMLInputElement).value)"
|
|
/>
|
|
<p v-if="error" class="text-xs text-red-600 dark:text-red-400">{{ error }}</p>
|
|
</div>
|
|
</template>
|