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
2 changes: 1 addition & 1 deletion src/components/calculator/ValidationMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function ValidationMessage({
<p
className={cn(
'text-xs mt-1',
type === 'error' && 'text-red-500',
type === 'error' && 'text-red-600',
type === 'warning' && 'text-amber-500',
type === 'info' && 'text-slate-600',
className
Expand Down
2 changes: 1 addition & 1 deletion src/components/form/FieldError.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function FieldError({
id={errorId}
className={cn(
'text-xs mt-1',
type === 'error' && 'text-red-500',
type === 'error' && 'text-red-600',
type === 'warning' && 'text-amber-500',
type === 'info' && 'text-slate-600',
className
Expand Down
2 changes: 1 addition & 1 deletion src/components/lab/SpectroscopeCalculator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export function SpectroscopeCalculator() {
placeholder="e.g., 694"
/>
{!isValid && wavelength && (
<p className="text-xs text-red-500 mt-1">Valid range: 380-780nm</p>
<p className="text-xs text-red-600 mt-1">Valid range: 380-780nm</p>
)}
</div>

Expand Down
6 changes: 6 additions & 0 deletions src/components/layout/Header.astro
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ const navItems = [
const currentPath = Astro.url.pathname;
---

<a
href="#main-content"
class="sr-only focus:not-sr-only focus:fixed focus:top-2 focus:left-2 focus:z-[100] focus:rounded-lg focus:bg-white focus:px-4 focus:py-2 focus:text-sm focus:font-medium focus:text-slate-900 focus:shadow-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-crystal-500 focus-visible:ring-offset-2"
>
Skip to content
</a>
<header class="sticky top-0 z-50 border-b border-slate-200 bg-white/80 backdrop-blur-lg">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div class="flex h-16 items-center justify-between">
Expand Down
16 changes: 8 additions & 8 deletions src/components/quiz/study/ConfidenceTap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,24 @@ const OPTIONS: ConfidenceOption[] = [
value: 'unsure',
label: 'Unsure',
shortcut: 'Q',
selectedClass: 'border-red-500 bg-red-50 text-red-700 dark:bg-red-950/40 dark:text-red-300 dark:border-red-500',
idleClass: 'border-slate-200 text-slate-700 hover:border-red-300 hover:bg-red-50/50 dark:border-slate-700 dark:text-slate-300 dark:hover:border-red-700',
selectedClass: 'border-red-500 bg-red-50 text-red-700',
idleClass: 'border-slate-200 text-slate-700 hover:border-red-300 hover:bg-red-50/50',
description: 'I guessed or am not confident',
},
{
value: 'fairly-sure',
label: 'Fairly sure',
shortcut: 'W',
selectedClass: 'border-amber-500 bg-amber-50 text-amber-700 dark:bg-amber-950/40 dark:text-amber-300 dark:border-amber-500',
idleClass: 'border-slate-200 text-slate-700 hover:border-amber-300 hover:bg-amber-50/50 dark:border-slate-700 dark:text-slate-300 dark:hover:border-amber-700',
selectedClass: 'border-amber-500 bg-amber-50 text-amber-700',
idleClass: 'border-slate-200 text-slate-700 hover:border-amber-300 hover:bg-amber-50/50',
description: 'I am reasonably confident',
},
{
value: 'certain',
label: 'Certain',
shortcut: 'E',
selectedClass: 'border-emerald-500 bg-emerald-50 text-emerald-700 dark:bg-emerald-950/40 dark:text-emerald-300 dark:border-emerald-500',
idleClass: 'border-slate-200 text-slate-700 hover:border-emerald-300 hover:bg-emerald-50/50 dark:border-slate-700 dark:text-slate-300 dark:hover:border-emerald-700',
selectedClass: 'border-emerald-500 bg-emerald-50 text-emerald-700',
idleClass: 'border-slate-200 text-slate-700 hover:border-emerald-300 hover:bg-emerald-50/50',
description: 'I am very confident in this answer',
},
];
Expand Down Expand Up @@ -96,7 +96,7 @@ export function ConfidenceTap({ value, onChange, disabled = false }: ConfidenceT
>
<legend
id="confidence-legend"
className="text-sm font-medium text-slate-700 dark:text-slate-300 mb-2"
className="text-sm font-medium text-slate-700 mb-2"
>
How confident are you?
</legend>
Expand Down Expand Up @@ -128,7 +128,7 @@ export function ConfidenceTap({ value, onChange, disabled = false }: ConfidenceT
<span>{opt.label}</span>
<kbd
aria-hidden="true"
className="text-xs opacity-50 font-sans not-italic"
className="hidden sm:inline text-xs opacity-50 font-sans not-italic"
>
[{opt.shortcut}]
</kbd>
Expand Down
8 changes: 4 additions & 4 deletions src/components/quiz/study/ExportImportPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export function ExportImportPanel({ store }: ExportImportPanelProps) {

return (
<Card padding="none" className="overflow-hidden">
<CardHeader className="px-6 pt-6 pb-4 mb-0 border-b border-slate-100 dark:border-slate-800">
<CardHeader className="px-6 pt-6 pb-4 mb-0 border-b border-slate-100">
<CardTitle as="h2">Export &amp; Import</CardTitle>
<CardDescription>
Back up your study history, schedule, and settings to a JSON file, or
Expand Down Expand Up @@ -158,11 +158,11 @@ export function ExportImportPanel({ store }: ExportImportPanelProps) {
className={cn(
'mx-6 mb-4 rounded-lg px-4 py-3 text-sm flex items-start gap-2',
toast.kind === 'success' &&
'bg-emerald-50 text-emerald-800 border border-emerald-200 dark:bg-emerald-950/40 dark:text-emerald-300 dark:border-emerald-800',
'bg-emerald-50 text-emerald-800 border border-emerald-200',
(toast.kind === 'warning') &&
'bg-amber-50 text-amber-800 border border-amber-200 dark:bg-amber-950/40 dark:text-amber-300 dark:border-amber-800',
'bg-amber-50 text-amber-800 border border-amber-200',
toast.kind === 'error' &&
'bg-red-50 text-red-800 border border-red-200 dark:bg-red-950/40 dark:text-red-300 dark:border-red-900',
'bg-red-50 text-red-800 border border-red-200',
)}
>
{toast.kind === 'success' && (
Expand Down
38 changes: 19 additions & 19 deletions src/components/quiz/study/LearnQuizWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,20 +153,20 @@ export function LearnQuizWidget({
>
<Card
padding="md"
className="border-emerald-200 dark:border-emerald-900/50 bg-emerald-50/40 dark:bg-emerald-950/30"
className="border-emerald-200 bg-emerald-50/40"
>
<CardContent className="flex items-start gap-4">
<span aria-hidden="true">
<CheckCircle2 className="w-7 h-7 text-emerald-600 dark:text-emerald-400 flex-shrink-0 mt-0.5" />
<CheckCircle2 className="w-7 h-7 text-emerald-600 flex-shrink-0 mt-0.5" />
</span>
<div>
<p className="font-semibold text-slate-900 dark:text-slate-100 text-sm tracking-tight">
<p className="font-semibold text-slate-900 text-sm tracking-tight">
Pretest complete
<span className="ml-2 font-mono text-xs font-normal text-emerald-700 dark:text-emerald-400">
<span className="ml-2 font-mono text-xs font-normal text-emerald-700">
{correctCount} / {visibleQuestions.length}
</span>
</p>
<p className="text-sm text-slate-600 dark:text-slate-400 mt-1.5 leading-relaxed">
<p className="text-sm text-slate-600 mt-1.5 leading-relaxed">
The article below covers each of these topics in depth. Pay particular
attention to any questions you got wrong; the pretesting effect makes
the content that follows measurably stickier.
Expand Down Expand Up @@ -195,10 +195,10 @@ export function LearnQuizWidget({
<aside className="my-6" aria-label={`Pretest, question ${currentIndex + 1} of ${visibleQuestions.length}`}>
<Card
padding="none"
className="overflow-hidden border-slate-200 dark:border-slate-800 dark:bg-slate-900"
className="overflow-hidden border-slate-200"
>
{/* Widget header — instrument-label strip */}
<div className="px-5 py-2.5 bg-slate-900 dark:bg-slate-950 border-b border-slate-900 dark:border-slate-800 flex items-center justify-between">
<div className="px-5 py-2.5 bg-slate-900 border-b border-slate-900 flex items-center justify-between">
<span
id={legendId}
className="flex items-baseline gap-2 text-sm font-semibold tracking-tight text-slate-50"
Expand All @@ -219,7 +219,7 @@ export function LearnQuizWidget({
<CardContent className="px-5 py-5">
{/* Helper line — guidance, not body */}
{currentIndex === 0 && !isSubmitted && (
<p className="text-sm text-slate-600 dark:text-slate-400 mb-4 leading-relaxed">
<p className="text-sm text-slate-600 mb-4 leading-relaxed">
<span aria-hidden="true" className="text-crystal-500 mr-1.5">—</span>
Try these before reading. Getting them wrong here makes the article
that follows stick better in memory.
Expand All @@ -228,7 +228,7 @@ export function LearnQuizWidget({

{/* Question stem */}
<p
className="text-base font-medium text-slate-900 dark:text-slate-100 leading-relaxed mb-4 tracking-tight"
className="text-base font-medium text-slate-900 leading-relaxed mb-4 tracking-tight"
aria-live="polite"
>
{currentQuestion.questionText}
Expand Down Expand Up @@ -260,15 +260,15 @@ export function LearnQuizWidget({
'focus:outline-none focus-visible:ring-2 focus-visible:ring-crystal-500 focus-visible:ring-offset-2',
'disabled:pointer-events-none',
isIdle &&
'border-slate-200 text-slate-800 hover:border-slate-300 hover:bg-slate-50 dark:border-slate-800 dark:text-slate-200 dark:hover:border-slate-700 dark:hover:bg-slate-800/40',
'border-slate-200 text-slate-800 hover:border-slate-300 hover:bg-slate-50',
isPickedIdle &&
'border-crystal-600 bg-crystal-50 text-crystal-900 ring-1 ring-crystal-600 dark:bg-crystal-950/40 dark:text-crystal-100 dark:border-crystal-500 dark:ring-crystal-500',
'border-crystal-600 bg-crystal-50 text-crystal-900 ring-1 ring-crystal-600',
isCorrectReveal &&
'border-emerald-600 bg-emerald-50 text-emerald-900 dark:bg-emerald-950/40 dark:text-emerald-100 dark:border-emerald-500',
'border-emerald-600 bg-emerald-50 text-emerald-900',
isWrongChosen &&
'border-red-500 bg-red-50 text-red-900 dark:bg-red-950/40 dark:text-red-100 dark:border-red-500',
'border-red-500 bg-red-50 text-red-900',
showCorrectness && !isThisCorrect && !isSelected &&
'border-slate-200 text-slate-500 dark:border-slate-800 dark:text-slate-500',
'border-slate-200 text-slate-500',
)}
>
<span className="inline-flex items-center gap-3">
Expand All @@ -277,15 +277,15 @@ export function LearnQuizWidget({
className={cn(
'inline-flex h-6 w-6 items-center justify-center rounded font-mono text-[11px] tracking-wider transition-colors duration-150 flex-shrink-0',
isIdle &&
'bg-slate-100 text-slate-600 group-hover:bg-slate-200 dark:bg-slate-800 dark:text-slate-400 dark:group-hover:bg-slate-700',
'bg-slate-100 text-slate-600 group-hover:bg-slate-200',
isPickedIdle &&
'bg-crystal-600 text-white dark:bg-crystal-500',
'bg-crystal-600 text-white',
isCorrectReveal &&
'bg-emerald-600 text-white dark:bg-emerald-500',
'bg-emerald-600 text-white',
isWrongChosen &&
'bg-red-600 text-white dark:bg-red-500',
'bg-red-600 text-white',
showCorrectness && !isThisCorrect && !isSelected &&
'bg-slate-100 text-slate-400 dark:bg-slate-800 dark:text-slate-600',
'bg-slate-100 text-slate-400',
)}
>
{letter}
Expand Down
42 changes: 17 additions & 25 deletions src/components/quiz/study/RationalePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,7 @@ export function RationalePanel({
<div
className={cn(
'mt-4 rounded-xl border-2 overflow-hidden',
correct
? 'border-emerald-200 dark:border-emerald-800'
: 'border-red-200 dark:border-red-900',
correct ? 'border-emerald-200' : 'border-red-200',
)}
role="region"
aria-label="Answer explanation"
Expand All @@ -83,17 +81,13 @@ export function RationalePanel({
className={cn(
'w-full flex items-center justify-between px-4 py-3 text-left',
'focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-crystal-500',
correct
? 'bg-emerald-50 dark:bg-emerald-950/40'
: 'bg-red-50 dark:bg-red-950/30',
correct ? 'bg-emerald-50' : 'bg-red-50',
)}
>
<span
className={cn(
'flex items-center gap-2 font-semibold text-sm',
correct
? 'text-emerald-700 dark:text-emerald-300'
: 'text-red-700 dark:text-red-300',
correct ? 'text-emerald-700' : 'text-red-700',
)}
>
{correct ? (
Expand All @@ -116,16 +110,16 @@ export function RationalePanel({
<div
id="rationale-body"
hidden={!expanded}
className="bg-white dark:bg-slate-900"
className="bg-white"
>
{/* Main rationale */}
<div className="px-4 py-3 text-sm text-slate-700 dark:text-slate-300 border-b border-slate-100 dark:border-slate-800">
<div className="px-4 py-3 text-sm text-slate-700 border-b border-slate-100">
{cleanCorrect}
</div>

{/* Per-option breakdown */}
{cleanOptions.length > 0 && (
<ul className="divide-y divide-slate-100 dark:divide-slate-800" aria-label="Option breakdown">
<ul className="divide-y divide-slate-100" aria-label="Option breakdown">
{cleanOptions.map((opt, idx) => {
const wasChosen = userPickedIndex === idx;
const isCorrect = opt.isCorrect;
Expand All @@ -134,23 +128,21 @@ export function RationalePanel({
key={idx}
className={cn(
'px-4 py-3 text-sm',
isCorrect && 'bg-emerald-50/50 dark:bg-emerald-950/20',
wasChosen && !isCorrect && 'bg-red-50/50 dark:bg-red-950/20',
isCorrect && 'bg-emerald-50/50',
wasChosen && !isCorrect && 'bg-red-50/50',
)}
>
<div className="flex items-start gap-2">
{isCorrect ? (
<CheckCircle2
className="w-4 h-4 text-emerald-600 dark:text-emerald-400 flex-shrink-0 mt-0.5"
className="w-4 h-4 text-emerald-600 flex-shrink-0 mt-0.5"
aria-label="Correct option"
/>
) : (
<XCircle
className={cn(
'w-4 h-4 flex-shrink-0 mt-0.5',
wasChosen
? 'text-red-600 dark:text-red-400'
: 'text-slate-600 dark:text-slate-600',
wasChosen ? 'text-red-600' : 'text-slate-600',
)}
aria-label={wasChosen ? 'Your choice, incorrect' : 'Incorrect option'}
/>
Expand All @@ -160,10 +152,10 @@ export function RationalePanel({
className={cn(
'font-medium',
isCorrect
? 'text-emerald-700 dark:text-emerald-300'
? 'text-emerald-700'
: wasChosen
? 'text-red-700 dark:text-red-300'
: 'text-slate-600 dark:text-slate-400',
? 'text-red-700'
: 'text-slate-600',
)}
>
{opt.text}
Expand All @@ -172,7 +164,7 @@ export function RationalePanel({
)}
</p>
{opt.rationale && (
<p className="mt-0.5 text-slate-600 dark:text-slate-400">{opt.rationale}</p>
<p className="mt-0.5 text-slate-600">{opt.rationale}</p>
)}
</div>
</div>
Expand All @@ -185,15 +177,15 @@ export function RationalePanel({
{/* Sources footer — single deduped citation line at the foot of the body */}
{sources.length > 0 && (
<p
className="px-4 py-2.5 text-xs text-slate-500 dark:text-slate-500 border-t border-slate-100 dark:border-slate-800 bg-slate-50/50 dark:bg-slate-900/40"
className="px-4 py-2.5 text-xs text-slate-500 border-t border-slate-100 bg-slate-50/50"
aria-label="Sources"
>
<span className="font-medium uppercase tracking-wider text-slate-600 dark:text-slate-400 mr-2">
<span className="font-medium uppercase tracking-wider text-slate-600 mr-2">
Sources
</span>
{sources.map((slug, idx) => (
<span key={slug}>
{idx > 0 && <span aria-hidden="true" className="mx-1.5 text-slate-400 dark:text-slate-700">·</span>}
{idx > 0 && <span aria-hidden="true" className="mx-1.5 text-slate-400">·</span>}
{citationLabel(slug)}
</span>
))}
Expand Down
Loading
Loading