Skip to content
Open
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
3 changes: 3 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2024-05-22 - [Accessibility in Forms and Collapsibles]
**Learning:** Inputs using only placeholders are inaccessible to screen readers. Always provide a `<label>` (can be `sr-only`). For collapsible content, `aria-expanded` and `aria-controls` are essential for communicating state changes.
**Action:** Use `sr-only` labels for all form inputs and ensure proper ARIA attributes for interactive collapsible elements in future components.
26 changes: 18 additions & 8 deletions components/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const ChatView: React.FC<ChatViewProps> = ({ messages, onSendMessage, onEnterVoi
{messages.length === 0 && (
<div className="h-full flex flex-col items-center justify-center text-center animate-in fade-in slide-in-from-bottom-12 duration-1000">
<div className="w-24 h-24 bg-black rounded-[2.5rem] flex items-center justify-center mb-10 shadow-2xl shadow-black/20 ring-4 ring-black/5 ring-offset-4 ring-offset-white">
<ChatIcon className="w-12 h-12 text-white" />
<ChatIcon className="w-12 h-12 text-white" aria-hidden="true" />
</div>
<h1 className="text-4xl font-semibold tracking-tight mb-4 text-gray-900">Wie funktioniert unser Wohnpro?</h1>
<p className="text-gray-400 max-w-sm mx-auto leading-relaxed text-lg">
Expand Down Expand Up @@ -80,6 +80,8 @@ const ChatView: React.FC<ChatViewProps> = ({ messages, onSendMessage, onEnterVoi
{/* Expand Hint Button */}
<button
onClick={() => toggleCitations(idx)}
aria-expanded={isExpanded}
aria-controls={`citations-${idx}`}
className={`flex items-center gap-2 px-4 py-2 rounded-full transition-all duration-300 group/hint border ${
isExpanded
? 'bg-black text-white border-black mb-4'
Expand All @@ -90,11 +92,13 @@ const ChatView: React.FC<ChatViewProps> = ({ messages, onSendMessage, onEnterVoi
<span className="text-[10px] font-black uppercase tracking-[0.2em]">
{msg.citations!.length} {msg.citations!.length === 1 ? 'Beleg' : 'Belege'} im Wohnpro Guide gefunden
</span>
<ArrowRightIcon className={`w-3 h-3 transition-transform duration-500 ${isExpanded ? 'rotate-90' : 'rotate-0'}`} />
<ArrowRightIcon className={`w-3 h-3 transition-transform duration-500 ${isExpanded ? 'rotate-90' : 'rotate-0'}`} aria-hidden="true" />
</button>

{/* Collapsible Citations View */}
<div className={`grid grid-cols-1 gap-3 w-full transition-all duration-500 ease-in-out overflow-hidden ${
<div
id={`citations-${idx}`}
className={`grid grid-cols-1 gap-3 w-full transition-all duration-500 ease-in-out overflow-hidden ${
isExpanded ? 'max-h-[1000px] opacity-100' : 'max-h-0 opacity-0 pointer-events-none'
}`}>
{msg.citations!.map((citation, i) => (
Expand All @@ -104,11 +108,11 @@ const ChatView: React.FC<ChatViewProps> = ({ messages, onSendMessage, onEnterVoi
className="group relative text-left bg-white border border-gray-100 rounded-[2rem] p-5 pr-14 flex gap-5 hover:border-black hover:shadow-xl transition-all duration-500 active:scale-[0.98] overflow-hidden"
>
<div className="absolute top-0 right-0 bottom-0 w-12 bg-gray-50 flex items-center justify-center border-l border-gray-100 group-hover:bg-black group-hover:border-black transition-all">
<ArrowRightIcon className="w-4 h-4 text-gray-300 group-hover:text-white transition-colors" />
<ArrowRightIcon className="w-4 h-4 text-gray-300 group-hover:text-white transition-colors" aria-hidden="true" />
</div>

<div className="p-3 bg-gray-50 rounded-xl shrink-0 group-hover:bg-green-50 transition-colors">
<DocIcon className="w-6 h-6 text-gray-400 group-hover:text-green-600" />
<DocIcon className="w-6 h-6 text-gray-400 group-hover:text-green-600" aria-hidden="true" />
</div>

<div className="flex-1 min-w-0">
Expand All @@ -133,7 +137,7 @@ const ChatView: React.FC<ChatViewProps> = ({ messages, onSendMessage, onEnterVoi

{isLoading && (
<div className="flex justify-start animate-in fade-in slide-in-from-left-4 duration-500">
<div className="bg-white border border-gray-100 px-7 py-5 rounded-[2.2rem] rounded-tl-none shadow-sm flex items-center gap-4">
<div role="status" aria-live="polite" className="bg-white border border-gray-100 px-7 py-5 rounded-[2.2rem] rounded-tl-none shadow-sm flex items-center gap-4">
<div className="flex gap-1.5">
<div className="w-2 h-2 bg-gray-300 rounded-full animate-bounce [animation-duration:1s]" />
<div className="w-2 h-2 bg-gray-300 rounded-full animate-bounce [animation-duration:1s] [animation-delay:0.2s]" />
Expand All @@ -150,7 +154,11 @@ const ChatView: React.FC<ChatViewProps> = ({ messages, onSendMessage, onEnterVoi
<form onSubmit={handleSubmit} className="relative group">
<div className="absolute inset-0 bg-black/5 blur-3xl rounded-full opacity-0 group-focus-within:opacity-100 transition-all duration-1000" />
<div className="relative flex items-center">
<label htmlFor="chat-input" className="sr-only">
Deine Frage zum Wohnpro
</label>
<input
id="chat-input"
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
Expand All @@ -164,19 +172,21 @@ const ChatView: React.FC<ChatViewProps> = ({ messages, onSendMessage, onEnterVoi
onClick={onEnterVoice}
className="p-3.5 text-gray-400 hover:text-green-600 transition-all rounded-full hover:bg-gray-50 active:scale-90"
title="Sprachmodus"
aria-label="Sprachmodus"
>
<MicIcon className="w-7 h-7" />
<MicIcon className="w-7 h-7" aria-hidden="true" />
</button>
<button
type="submit"
disabled={!input.trim() || isLoading}
aria-label="Nachricht senden"
className={`p-4 rounded-full transition-all active:scale-90 ${
input.trim() && !isLoading
? 'bg-black text-white shadow-xl shadow-black/20 hover:bg-gray-900'
: 'bg-gray-50 text-gray-300 cursor-not-allowed'
}`}
>
<ArrowRightIcon className="w-7 h-7" />
<ArrowRightIcon className="w-7 h-7" aria-hidden="true" />
</button>
</div>
</div>
Expand Down
14 changes: 12 additions & 2 deletions components/LoginView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const LoginView: React.FC<LoginViewProps> = ({ onLogin, error: externalError })
<div className="w-full max-w-sm text-center">
<div className="mb-12">
<div className="w-16 h-16 bg-black rounded-3xl mx-auto flex items-center justify-center mb-6 shadow-xl shadow-black/10">
<svg className="w-8 h-8 text-white" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
<svg aria-hidden="true" className="w-8 h-8 text-white" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
<polyline points="9 22 9 12 15 12 15 22" />
</svg>
Expand All @@ -36,14 +36,24 @@ const LoginView: React.FC<LoginViewProps> = ({ onLogin, error: externalError })

<form onSubmit={handleSubmit} className="space-y-4">
<div className="relative group">
<label htmlFor="email-input" className="sr-only">
Wohnpro E-Mail Adresse
</label>
<input
id="email-input"
type="email"
value={email}
onChange={(e) => { setEmail(e.target.value); setError(''); }}
placeholder="Wohnpro E-Mail Adresse"
aria-invalid={!!error}
aria-describedby={error ? "email-error" : undefined}
className={`w-full bg-gray-50 border ${error ? 'border-red-200' : 'border-gray-100'} rounded-2xl px-6 py-4 focus:outline-none focus:ring-2 focus:ring-black/5 transition-all text-lg placeholder:text-gray-300`}
/>
{error && <p className="mt-2 text-sm text-red-500 text-left px-2">{error}</p>}
{error && (
<p id="email-error" role="alert" className="mt-2 text-sm text-red-500 text-left px-2">
{error}
</p>
)}
</div>

<button
Expand Down