chore: Tailwind CSS v3 → v4 migration (#99)

* chore: run Tailwind v4 upgrade tool (Phase 1)

- Upgraded tailwindcss v3 → v4.2.1, postcss plugin to @tailwindcss/postcss
- Deleted tailwind.config.js, migrated theme to CSS @theme block in index.css
- Replaced @tailwind directives with @import 'tailwindcss'
- Added @custom-variant dark, @utility blocks for custom utilities
- Updated class names across 128 files (shadow-sm → shadow-xs, etc.)
- Removed autoprefixer (built into v4)
- Added migration plan doc

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: switch from @tailwindcss/postcss to @tailwindcss/vite (Phase 2)

- Replaced @tailwindcss/postcss with @tailwindcss/vite plugin
- Deleted postcss.config.js (no longer needed)
- Tailwind now runs as a native Vite plugin for faster HMR

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: convert to OKLCH colors, move keyframes into @theme (Phase 3-4)

- Replaced all HSL color indirection with direct OKLCH values in @theme
- Moved all keyframes inside @theme block (v4 pattern)
- Eliminated hsl(var(--x)) double-indirection across 17 component files
- Replaced hsl() inline styles with var(--color-*) theme references
- Cleaned up redundant rdp-* utility blocks
- Fixed @custom-variant dark syntax to use :where()
- Added sidebar/glass/shadow vars as OKLCH in :root

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit was merged in pull request #99.
This commit is contained in:
chihlasm
2026-03-07 22:10:44 -05:00
committed by GitHub
parent 732ccba966
commit d365c38b61
137 changed files with 1922 additions and 1709 deletions

View File

@@ -74,7 +74,7 @@ export function CSATModal({ isOpen, onClose, sessionId }: CSATModalProps) {
placeholder="Any comments? (optional)"
maxLength={500}
rows={3}
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20 resize-none"
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20 resize-none"
/>
{/* Actions */}

View File

@@ -62,7 +62,7 @@ export function ContinuationModal({
'hover:border-border hover:bg-accent'
)}
>
<div className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-accent">
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-accent">
{nodeTypeIcons[node.type]}
</div>
<div className="min-w-0 flex-1">
@@ -71,7 +71,7 @@ export function ContinuationModal({
{nodeTypeLabels[node.type]}
</p>
</div>
<ArrowRight className="h-4 w-4 flex-shrink-0 text-muted-foreground" />
<ArrowRight className="h-4 w-4 shrink-0 text-muted-foreground" />
</button>
))}
</div>
@@ -98,7 +98,7 @@ export function ContinuationModal({
'hover:border-amber-500 hover:bg-amber-500/10'
)}
>
<div className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full bg-amber-500/10">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-amber-500/10">
<GitBranch className="h-5 w-5 text-amber-500" />
</div>
<div className="flex-1">
@@ -111,7 +111,7 @@ export function ContinuationModal({
{/* Warning */}
<div className="mt-3 flex items-start gap-2 rounded-md bg-yellow-400/10 p-3">
<AlertTriangle className="mt-0.5 h-4 w-4 flex-shrink-0 text-yellow-400" />
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-yellow-400" />
<p className="text-sm text-yellow-400">
You'll need to complete this branch manually or mark the issue as resolved.
Custom branches can be saved as a personal tree when your session ends.

View File

@@ -141,7 +141,7 @@ export function ExportPreviewModal({
className={cn(
'h-96 w-full resize-y rounded-md border border-border bg-card p-4',
'font-mono text-sm text-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
@@ -153,7 +153,7 @@ export function ExportPreviewModal({
className={cn(
'flex items-center gap-2 rounded-md border border-border px-3 py-2 text-sm font-medium',
'text-muted-foreground hover:bg-accent hover:text-foreground',
'focus:outline-none focus:ring-2 focus:ring-primary/20'
'focus:outline-hidden focus:ring-2 focus:ring-primary/20'
)}
>
{copied ? (
@@ -173,7 +173,7 @@ export function ExportPreviewModal({
onClick={handleDownload}
className={cn(
'flex items-center gap-2 rounded-md bg-gradient-brand text-white shadow-lg shadow-primary/20 px-3 py-2 text-sm font-medium',
'hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-primary/20'
'hover:opacity-90 focus:outline-hidden focus:ring-2 focus:ring-primary/20'
)}
>
<Download className="h-4 w-4" />

View File

@@ -83,7 +83,7 @@ export function ForkTreeModal({
<Modal isOpen={isOpen} onClose={onClose} title="Save Custom Tree?" footer={footer}>
<div className="space-y-4">
<div className="flex items-start gap-3 rounded-lg bg-accent/50 p-4">
<div className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full bg-accent">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-accent">
<GitFork className="h-5 w-5 text-foreground" />
</div>
<div>
@@ -107,7 +107,7 @@ export function ForkTreeModal({
placeholder="My Custom Tree"
className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20'
'focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20'
)}
/>
</div>
@@ -124,7 +124,7 @@ export function ForkTreeModal({
rows={3}
className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20',
'focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20',
'resize-none'
)}
/>

View File

@@ -33,7 +33,7 @@ export function SaveSessionAsTreeModal({
}
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm">
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs">
<div className="bg-card border border-border w-full max-w-lg rounded-2xl p-6 shadow-lg">
{/* Header */}
<div className="mb-4 flex items-center justify-between">
@@ -70,7 +70,7 @@ export function SaveSessionAsTreeModal({
className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:opacity-50'
)}
/>
@@ -91,7 +91,7 @@ export function SaveSessionAsTreeModal({
className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:opacity-50'
)}
/>

View File

@@ -141,7 +141,7 @@ export function ScratchpadSidebar({ sessionId, initialContent, onSave, onOpenCha
{/* Mobile backdrop */}
{!isCollapsed && (
<div
className="fixed inset-0 z-30 bg-black/80 backdrop-blur-sm sm:hidden"
className="fixed inset-0 z-30 bg-black/80 backdrop-blur-xs sm:hidden"
onClick={() => setIsCollapsed(true)}
aria-hidden="true"
/>
@@ -203,7 +203,7 @@ export function ScratchpadSidebar({ sessionId, initialContent, onSave, onOpenCha
className={cn(
'h-full min-h-[200px] w-full resize-none rounded-md border-0 bg-transparent p-0 text-sm',
'text-foreground placeholder:text-muted-foreground',
'focus:outline-none focus:ring-0'
'focus:outline-hidden focus:ring-0'
)}
/>
)}

View File

@@ -102,7 +102,7 @@ export function SessionFilters({ filters, onChange, onClear, trees }: SessionFil
className={cn(
'w-full rounded-md border border-border bg-card py-2 pl-9 pr-3',
'text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
</div>
@@ -118,7 +118,7 @@ export function SessionFilters({ filters, onChange, onClear, trees }: SessionFil
className={cn(
'w-full rounded-md border border-border bg-card py-2 pl-9 pr-3',
'text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
</div>
@@ -129,7 +129,7 @@ export function SessionFilters({ filters, onChange, onClear, trees }: SessionFil
onChange={(e) => handleFilterChange('treeName', e.target.value)}
className={cn(
'rounded-md border border-border bg-card px-3 py-2',
'text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'sm:min-w-[200px]'
)}
>

View File

@@ -111,7 +111,7 @@ export function SessionOutcomeModal({
className={cn(
'mt-1 block w-full rounded-md border border-border bg-card px-3 py-2',
'text-sm text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
</div>
@@ -126,7 +126,7 @@ export function SessionOutcomeModal({
className={cn(
'mt-1 block w-full rounded-md border border-border bg-card px-3 py-2',
'text-sm text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
</div>

View File

@@ -135,7 +135,7 @@ export function SessionTimeline({
{decisions.map((decision, index) => (
<div key={index} className="ml-1 border-l-2 border-border pl-6">
<div className="relative">
<span className="absolute -left-[1.625rem] top-1 h-2 w-2 rounded-full bg-muted-foreground" />
<span className="absolute -left-6.5 top-1 h-2 w-2 rounded-full bg-muted-foreground" />
<div className="bg-card border border-border rounded-xl p-4">
<div className="flex items-start justify-between gap-2">
<div className="flex-1">

View File

@@ -181,7 +181,7 @@ export function ShareSessionModal({ sessionId, sessionLabel, isOpen, onClose }:
<div className="fixed inset-0 z-50 flex items-center justify-center">
{/* Backdrop */}
<div
className="absolute inset-0 bg-black/80 backdrop-blur-sm"
className="absolute inset-0 bg-black/80 backdrop-blur-xs"
onClick={onClose}
/>
@@ -265,7 +265,7 @@ export function ShareSessionModal({ sessionId, sessionLabel, isOpen, onClose }:
placeholder="e.g. Training link, Customer escalation"
className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground placeholder-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
maxLength={100}
/>
@@ -299,8 +299,8 @@ export function ShareSessionModal({ sessionId, sessionLabel, isOpen, onClose }:
onChange={(e) => setCustomDatetime(e.target.value)}
className={cn(
'mt-2 w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'[color-scheme:dark]'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'scheme-dark'
)}
/>
)}

View File

@@ -76,7 +76,7 @@ export function SharedSessionTreePreview({
return (
<div className="bg-card border border-border rounded-2xl">
<div className="sticky top-0 z-10 rounded-t-2xl border-b border-border bg-black/80 px-6 py-4 backdrop-blur">
<div className="sticky top-0 z-10 rounded-t-2xl border-b border-border bg-black/80 px-6 py-4 backdrop-blur-sm">
<h3 className="text-sm font-semibold text-foreground">Tree Structure</h3>
</div>
<div className="max-h-[600px] overflow-y-auto py-2">

View File

@@ -77,7 +77,7 @@ export function StepRatingModal({
const getRating = (stepId: string) => ratings.get(stepId)
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm p-4">
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs p-4">
<div className="bg-card border border-border w-full max-w-2xl max-h-[90vh] flex flex-col rounded-2xl shadow-lg">
{/* Header */}
<div className="flex items-center justify-between border-b border-border px-6 py-4">
@@ -174,7 +174,7 @@ export function StepRatingModal({
className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:opacity-50'
)}
/>

View File

@@ -21,7 +21,7 @@ export function VariablePromptModal({ prompt, onSubmit, onCancel }: VariableProm
}
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm">
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs">
<div className="bg-card border border-border w-full max-w-md rounded-2xl p-6 shadow-lg">
<h2 className="mb-1 text-lg font-semibold text-foreground">Input Required</h2>
<p className="mb-4 text-sm text-muted-foreground">
@@ -40,7 +40,7 @@ export function VariablePromptModal({ prompt, onSubmit, onCancel }: VariableProm
autoFocus
className={cn(
'w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
/>