phase 1: motion language

This commit is contained in:
Richard R 2026-05-31 19:25:40 -06:00
parent 7bd798b9d1
commit 11e138ae36
41 changed files with 181 additions and 184 deletions

View file

@ -173,7 +173,7 @@ export default function EPUBPage() {
<p className="text-red-500 mb-4">{error}</p> <p className="text-red-500 mb-4">{error}</p>
<Link <Link
href="/app" href="/app"
className="inline-flex items-center px-3 py-1 bg-base text-foreground rounded-lg hover:bg-offbase transition-all duration-200 ease-in-out hover:scale-[1.04] hover:text-accent" className="inline-flex items-center px-3 py-1 bg-base text-foreground rounded-lg hover:bg-offbase transition duration-base ease-standard hover:text-accent"
> >
<svg className="w-4 h-4 mr-2 text-muted" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-4 h-4 mr-2 text-muted" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
@ -190,7 +190,7 @@ export default function EPUBPage() {
left={ left={
<Link <Link
href="/app" href="/app"
className="inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition-all duration-200 ease-in-out hover:scale-[1.04] hover:text-accent" className="inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition duration-base ease-standard hover:text-accent"
aria-label="Back to documents" aria-label="Back to documents"
> >
<svg className="w-3 h-3 mr-2" fill="currentColor" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-3 h-3 mr-2" fill="currentColor" stroke="currentColor" viewBox="0 0 24 24">

View file

@ -160,7 +160,7 @@ export default function HTMLPage() {
<p className="text-red-500 mb-4">{error}</p> <p className="text-red-500 mb-4">{error}</p>
<Link <Link
href="/app" href="/app"
className="inline-flex items-center px-3 py-1 bg-base text-foreground rounded-lg hover:bg-offbase transition-all duration-200 ease-in-out hover:scale-[1.04] hover:text-accent" className="inline-flex items-center px-3 py-1 bg-base text-foreground rounded-lg hover:bg-offbase transition duration-base ease-standard hover:text-accent"
> >
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
@ -177,7 +177,7 @@ export default function HTMLPage() {
left={ left={
<Link <Link
href="/app" href="/app"
className="inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition-all duration-200 ease-in-out hover:scale-[1.04] hover:text-accent" className="inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition duration-base ease-standard hover:text-accent"
aria-label="Back to documents" aria-label="Back to documents"
> >
<svg className="w-3 h-3 mr-2" fill="currentColor" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-3 h-3 mr-2" fill="currentColor" stroke="currentColor" viewBox="0 0 24 24">

View file

@ -255,7 +255,7 @@ export default function PDFViewerPage() {
<Link <Link
href="/app" href="/app"
onClick={handleBackToDocuments} onClick={handleBackToDocuments}
className="inline-flex items-center px-3 py-1 bg-base text-foreground rounded-lg hover:bg-offbase transition-all duration-200 ease-in-out hover:scale-[1.04] hover:text-accent" className="inline-flex items-center px-3 py-1 bg-base text-foreground rounded-lg hover:bg-offbase transition duration-base ease-standard hover:text-accent"
> >
<svg className="w-4 h-4 mr-2 text-muted" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-4 h-4 mr-2 text-muted" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
@ -309,7 +309,7 @@ export default function PDFViewerPage() {
return ( return (
<div className="h-full w-full bg-base"> <div className="h-full w-full bg-base">
<div className={`mx-auto flex h-full items-center px-4 py-6 transition-all duration-300 ease-out ${showDetailedParseLoader ? 'max-w-lg' : 'max-w-md'}`}> <div className={`mx-auto flex h-full items-center px-4 py-6 transition duration-slow ease-standard ${showDetailedParseLoader ? 'max-w-lg' : 'max-w-md'}`}>
{showDetailedParseLoader ? ( {showDetailedParseLoader ? (
<div className="w-full rounded-xl border border-offbase bg-offbase/95 shadow-sm overflow-hidden"> <div className="w-full rounded-xl border border-offbase bg-offbase/95 shadow-sm overflow-hidden">
<div className="h-1 bg-[linear-gradient(90deg,var(--accent),transparent_80%)]" /> <div className="h-1 bg-[linear-gradient(90deg,var(--accent),transparent_80%)]" />
@ -331,7 +331,7 @@ export default function PDFViewerPage() {
</div> </div>
<div className="h-2 w-full rounded-full bg-offbase overflow-hidden"> <div className="h-2 w-full rounded-full bg-offbase overflow-hidden">
<div <div
className="h-full bg-accent transition-all duration-300 ease-out" className="h-full bg-accent transition duration-slow ease-standard"
style={{ width: `${hasMeasuredProgress ? progressPercent : 6}%` }} style={{ width: `${hasMeasuredProgress ? progressPercent : 6}%` }}
/> />
</div> </div>
@ -354,7 +354,7 @@ export default function PDFViewerPage() {
</div> </div>
</div> </div>
) : ( ) : (
<div className="w-full rounded-xl border border-offbase bg-offbase/95 p-4 shadow-sm transition-all duration-300 ease-out overflow-hidden"> <div className="w-full rounded-xl border border-offbase bg-offbase/95 p-4 shadow-sm transition duration-slow ease-standard overflow-hidden">
<div className="h-0.5 -mx-4 -mt-4 mb-3 bg-[linear-gradient(90deg,var(--accent),transparent_75%)]" /> <div className="h-0.5 -mx-4 -mt-4 mb-3 bg-[linear-gradient(90deg,var(--accent),transparent_75%)]" />
<div className="flex items-start justify-between gap-3"> <div className="flex items-start justify-between gap-3">
<div className="min-w-0"> <div className="min-w-0">
@ -384,7 +384,7 @@ export default function PDFViewerPage() {
<Link <Link
href="/app" href="/app"
onClick={handleBackToDocuments} onClick={handleBackToDocuments}
className="inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition-all duration-200 ease-in-out hover:scale-[1.04] hover:text-accent" className="inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition duration-base ease-standard hover:text-accent"
aria-label="Back to documents" aria-label="Back to documents"
> >
<svg className="w-3 h-3 mr-2" fill="currentColor" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-3 h-3 mr-2" fill="currentColor" stroke="currentColor" viewBox="0 0 24 24">

View file

@ -188,7 +188,7 @@ function SignInContent() {
type="submit" type="submit"
disabled={isAnyLoading} disabled={isAnyLoading}
onClick={handleSignIn} onClick={handleSignIn}
className={buttonClass({ variant: 'primary', size: 'md', className: 'w-full hover:scale-[1.02]' })} className={buttonClass({ variant: 'primary', size: 'md', className: 'w-full' })}
> >
{loadingEmail ? <LoadingSpinner className="w-4 h-4 mx-auto" /> : 'Connect'} {loadingEmail ? <LoadingSpinner className="w-4 h-4 mx-auto" /> : 'Connect'}
</Button> </Button>
@ -202,7 +202,7 @@ function SignInContent() {
className={buttonClass({ className={buttonClass({
variant: 'outline', variant: 'outline',
size: 'md', size: 'md',
className: 'w-full hover:scale-[1.02] flex items-center justify-center gap-2', className: 'w-full flex items-center justify-center gap-2',
})} })}
> >
{loadingGithub ? ( {loadingGithub ? (
@ -222,7 +222,7 @@ function SignInContent() {
type="button" type="button"
disabled={isAnyLoading} disabled={isAnyLoading}
onClick={handleAnonymousContinue} onClick={handleAnonymousContinue}
className={buttonClass({ variant: 'outline', size: 'md', className: 'w-full hover:scale-[1.02]' })} className={buttonClass({ variant: 'outline', size: 'md', className: 'w-full' })}
> >
{loadingAnonymous ? <LoadingSpinner className="w-4 h-4 mx-auto" /> : 'Continue anonymously'} {loadingAnonymous ? <LoadingSpinner className="w-4 h-4 mx-auto" /> : 'Continue anonymously'}
</Button> </Button>

View file

@ -234,7 +234,7 @@ export default function SignUpPage() {
type="submit" type="submit"
disabled={loading} disabled={loading}
onClick={handleSignUp} onClick={handleSignUp}
className={buttonClass({ variant: 'primary', size: 'md', className: 'w-full hover:scale-[1.02]' })} className={buttonClass({ variant: 'primary', size: 'md', className: 'w-full' })}
> >
{loading ? <LoadingSpinner className="w-4 h-4 mx-auto" /> : 'Create Account'} {loading ? <LoadingSpinner className="w-4 h-4 mx-auto" /> : 'Create Account'}
</Button> </Button>

View file

@ -560,7 +560,7 @@ export function AudiobookExportModal({
disabled={chapters.length > 0 || settingsLocked} disabled={chapters.length > 0 || settingsLocked}
> >
<div className="relative"> <div className="relative">
<ListboxButton className="relative cursor-pointer rounded-lg bg-base py-1.5 pl-3 pr-10 text-left text-foreground focus:outline-none focus:ring-2 focus:ring-accent transform transition-transform duration-200 ease-in-out hover:scale-[1.01] hover:text-accent w-full"> <ListboxButton className="relative cursor-pointer rounded-lg bg-base py-1.5 pl-3 pr-10 text-left text-foreground focus:outline-none focus:ring-2 focus:ring-accent transform transition-transform duration-base ease-standard hover:text-accent w-full">
<span className="block truncate text-sm font-medium">{format.toUpperCase()}</span> <span className="block truncate text-sm font-medium">{format.toUpperCase()}</span>
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"> <span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<ChevronUpDownIcon className="h-4 w-4 text-muted" /> <ChevronUpDownIcon className="h-4 w-4 text-muted" />
@ -568,7 +568,7 @@ export function AudiobookExportModal({
</ListboxButton> </ListboxButton>
<Transition <Transition
as={Fragment} as={Fragment}
leave="transition ease-in duration-100" leave="transition ease-standard duration-fast"
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
@ -673,7 +673,7 @@ export function AudiobookExportModal({
className="flex-1 inline-flex justify-center rounded-lg bg-accent px-3 py-2 text-sm className="flex-1 inline-flex justify-center rounded-lg bg-accent px-3 py-2 text-sm
font-medium text-background hover:bg-secondary-accent focus:outline-none font-medium text-background hover:bg-secondary-accent focus:outline-none
focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
transform transition-transform duration-200 ease-in-out hover:scale-[1.01] disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100" transform transition-transform duration-base ease-standard disabled:opacity-50 disabled:cursor-not-allowed"
> >
Start Generation Start Generation
</Button> </Button>
@ -685,7 +685,7 @@ export function AudiobookExportModal({
className="flex-1 inline-flex justify-center rounded-lg bg-accent px-3 py-2 text-sm className="flex-1 inline-flex justify-center rounded-lg bg-accent px-3 py-2 text-sm
font-medium text-background hover:bg-secondary-accent focus:outline-none font-medium text-background hover:bg-secondary-accent focus:outline-none
focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
transform transition-transform duration-200 ease-in-out hover:scale-[1.01] disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100" transform transition-transform duration-base ease-standard disabled:opacity-50 disabled:cursor-not-allowed"
> >
Resume Resume
</Button> </Button>
@ -697,7 +697,7 @@ export function AudiobookExportModal({
className="inline-flex justify-center rounded-lg border border-red-500 bg-transparent px-3 py-2 text-sm className="inline-flex justify-center rounded-lg border border-red-500 bg-transparent px-3 py-2 text-sm
font-medium text-red-500 hover:bg-red-500 hover:text-background focus:outline-none font-medium text-red-500 hover:bg-red-500 hover:text-background focus:outline-none
focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:ring-offset-2 focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:ring-offset-2
transform transition-transform duration-200 ease-in-out hover:scale-[1.01]" transform transition-transform duration-base ease-standard"
title="Delete all generated chapters/pages for this document" title="Delete all generated chapters/pages for this document"
> >
Reset Reset
@ -760,7 +760,7 @@ export function AudiobookExportModal({
<Button <Button
onClick={() => handleRegenerateChapter(chapter)} onClick={() => handleRegenerateChapter(chapter)}
disabled={regeneratingChapter !== null || chapter.status === 'generating' || isGenerating} disabled={regeneratingChapter !== null || chapter.status === 'generating' || isGenerating}
className="inline-flex items-center justify-center rounded-full bg-offbase text-accent hover:bg-accent/20 p-1.5 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent transform transition-transform duration-200 ease-in-out hover:scale-[1.04] disabled:opacity-50 disabled:cursor-not-allowed" className="inline-flex items-center justify-center rounded-full bg-offbase text-accent hover:bg-accent/20 p-1.5 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent transform transition-transform duration-base ease-standard disabled:opacity-50 disabled:cursor-not-allowed"
title={chapter.status === 'generating' ? 'Generating...' : 'Regenerate this chapter'} title={chapter.status === 'generating' ? 'Generating...' : 'Regenerate this chapter'}
> >
<RefreshIcon className={`h-4 w-4 ${regeneratingChapter === chapter.index || chapter.status === 'generating' ? 'animate-spin' : ''}`} /> <RefreshIcon className={`h-4 w-4 ${regeneratingChapter === chapter.index || chapter.status === 'generating' ? 'animate-spin' : ''}`} />
@ -782,17 +782,17 @@ export function AudiobookExportModal({
{((onRegenerateChapter && !isGenerating) || chapter.status === 'completed') && ( {((onRegenerateChapter && !isGenerating) || chapter.status === 'completed') && (
<Menu as="div" className="relative inline-block text-left"> <Menu as="div" className="relative inline-block text-left">
<MenuButton <MenuButton
className="inline-flex items-center justify-center rounded-md p-1.5 hover:bg-background focus:outline-none focus-visible:ring-2 focus-visible:ring-accent text-muted hover:text-foreground transform transition-transform duration-200 ease-in-out hover:scale-[1.04]" className="inline-flex items-center justify-center rounded-md p-1.5 hover:bg-background focus:outline-none focus-visible:ring-2 focus-visible:ring-accent text-muted hover:text-foreground transform transition-transform duration-base ease-standard"
title="Chapter actions" title="Chapter actions"
> >
<DotsVerticalIcon className="h-5 w-5" /> <DotsVerticalIcon className="h-5 w-5" />
</MenuButton> </MenuButton>
<Transition <Transition
as={Fragment} as={Fragment}
enter="transition ease-out duration-100" enter="transition ease-standard duration-fast"
enterFrom="transform opacity-0 scale-95" enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100" enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75" leave="transition ease-standard duration-fast"
leaveFrom="transform opacity-100 scale-100" leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95" leaveTo="transform opacity-0 scale-95"
> >
@ -873,10 +873,10 @@ export function AudiobookExportModal({
onClick={handleDownloadComplete} onClick={handleDownloadComplete}
disabled={isCombining} disabled={isCombining}
className="w-full inline-flex justify-center items-center space-x-2 rounded-lg bg-accent px-3 py-1.5 text-sm className="w-full inline-flex justify-center items-center space-x-2 rounded-lg bg-accent px-3 py-1.5 text-sm
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100 disabled:opacity-50 disabled:cursor-not-allowed
font-medium text-background hover:bg-secondary-accent focus:outline-none font-medium text-background hover:bg-secondary-accent focus:outline-none
focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-background" transform transition-transform duration-base ease-standard hover:text-background"
> >
<DownloadIcon className="h-5 w-5" /> <DownloadIcon className="h-5 w-5" />
<span>{isCombining ? 'Combining chapters...' : `Full Download (${format.toUpperCase()})`}</span> <span>{isCombining ? 'Combining chapters...' : `Full Download (${format.toUpperCase()})`}</span>

View file

@ -24,7 +24,7 @@ export function CodeBlock({ children }: { children: string }) {
className={buttonClass({ className={buttonClass({
variant: 'secondary', variant: 'secondary',
size: 'icon', size: 'icon',
className: 'absolute top-2 right-2 h-7 w-7 opacity-0 group-hover:opacity-100 focus:opacity-100 hover:scale-[1.05]', className: 'absolute top-2 right-2 h-7 w-7 opacity-0 group-hover:opacity-100 focus:opacity-100',
})} })}
title="Copy to clipboard" title="Copy to clipboard"
> >

View file

@ -80,7 +80,7 @@ export function ColorPicker({ value, field, label, onChange }: ColorPickerProps)
<Popover className="relative flex items-center"> <Popover className="relative flex items-center">
<PopoverButton className="cursor-pointer group focus:outline-none"> <PopoverButton className="cursor-pointer group focus:outline-none">
<div <div
className="w-6 h-6 rounded-full border-2 transition-all duration-150 group-hover:scale-110 group-focus-visible:ring-2 group-focus-visible:ring-offset-1" className="w-6 h-6 rounded-full border-2 transition duration-fast group-focus-visible:ring-2 group-focus-visible:ring-offset-1"
style={{ style={{
backgroundColor: value, backgroundColor: value,
borderColor: isLightColor(value) ? '#00000022' : '#ffffff22', borderColor: isLightColor(value) ? '#00000022' : '#ffffff22',
@ -92,7 +92,7 @@ export function ColorPicker({ value, field, label, onChange }: ColorPickerProps)
anchor="bottom start" anchor="bottom start"
transition transition
className="z-[60] mt-2 w-56 rounded-xl shadow-xl border border-offbase bg-background p-3 space-y-3 className="z-[60] mt-2 w-56 rounded-xl shadow-xl border border-offbase bg-background p-3 space-y-3
transition duration-150 ease-out data-[closed]:opacity-0 data-[closed]:scale-95" transition duration-fast ease-standard data-[closed]:opacity-0 data-[closed]:scale-95"
> >
{/* Label */} {/* Label */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
@ -107,7 +107,7 @@ export function ColorPicker({ value, field, label, onChange }: ColorPickerProps)
className="p-1" className="p-1"
aria-label="Open system color picker" aria-label="Open system color picker"
> >
<PaletteIcon className="w-4 h-4 text-muted transform transition-transform duration-200 ease-in-out hover:scale-[1.09] hover:text-accent" /> <PaletteIcon className="w-4 h-4 text-muted transform transition-transform duration-base ease-standard hover:text-accent" />
</button> </button>
<input <input
ref={nativeRef} ref={nativeRef}
@ -130,7 +130,7 @@ export function ColorPicker({ value, field, label, onChange }: ColorPickerProps)
key={color} key={color}
type="button" type="button"
onClick={() => onChange(color)} onClick={() => onChange(color)}
className="group/swatch relative w-full aspect-square rounded-full transition-transform duration-100 hover:scale-125 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1" className="group/swatch relative w-full aspect-square rounded-full transition-transform duration-fast focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1"
style={{ style={{
backgroundColor: color, backgroundColor: color,
boxShadow: selected ? '0 0 0 2px var(--background), 0 0 0 4px var(--foreground)' : undefined, boxShadow: selected ? '0 0 0 2px var(--background), 0 0 0 4px var(--foreground)' : undefined,

View file

@ -41,10 +41,10 @@ export function ConfirmDialog({
> >
<TransitionChild <TransitionChild
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-standard duration-slow"
enterFrom="opacity-0" enterFrom="opacity-0"
enterTo="opacity-100" enterTo="opacity-100"
leave="ease-in duration-200" leave="ease-standard duration-base"
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
@ -55,14 +55,14 @@ export function ConfirmDialog({
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4"> <div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
<TransitionChild <TransitionChild
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-standard duration-slow"
enterFrom="opacity-0 scale-95" enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100" enterTo="opacity-100 scale-100"
leave="ease-in duration-200" leave="ease-standard duration-base"
leaveFrom="opacity-100 scale-100" leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95" leaveTo="opacity-0 scale-95"
> >
<DialogPanel role='dialog' className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition-all"> <DialogPanel role='dialog' className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition">
<DialogTitle <DialogTitle
as="h3" as="h3"
className="text-lg font-semibold leading-6 text-foreground" className="text-lg font-semibold leading-6 text-foreground"
@ -79,7 +79,6 @@ export function ConfirmDialog({
className={buttonClass({ className={buttonClass({
variant: 'outline', variant: 'outline',
size: 'sm', size: 'sm',
className: 'hover:scale-[1.04]',
})} })}
onClick={onClose} onClick={onClose}
> >
@ -90,7 +89,7 @@ export function ConfirmDialog({
className={buttonClass({ className={buttonClass({
variant: isDangerous ? 'danger' : 'primary', variant: isDangerous ? 'danger' : 'primary',
size: 'sm', size: 'sm',
className: 'text-wrap hover:scale-[1.04]', className: 'text-wrap',
})} })}
onClick={onConfirm} onClick={onConfirm}
> >

View file

@ -35,10 +35,10 @@ export function CookieConsentBanner() {
<Transition <Transition
as="div" as="div"
show={show} show={show}
enter="transition ease-out duration-300 transform" enter="transition ease-standard duration-slow transform"
enterFrom="translate-y-full opacity-0" enterFrom="translate-y-full opacity-0"
enterTo="translate-y-0 opacity-100" enterTo="translate-y-0 opacity-100"
leave="transition ease-in duration-200 transform" leave="transition ease-standard duration-base transform"
leaveFrom="translate-y-0 opacity-100" leaveFrom="translate-y-0 opacity-100"
leaveTo="translate-y-full opacity-0" leaveTo="translate-y-full opacity-0"
className="fixed bottom-0 left-0 right-0 z-[60] px-4 pt-4 pb-[max(1rem,env(safe-area-inset-bottom))] md:px-6 md:pt-6 md:pb-[max(1.5rem,env(safe-area-inset-bottom))]" className="fixed bottom-0 left-0 right-0 z-[60] px-4 pt-4 pb-[max(1rem,env(safe-area-inset-bottom))] md:px-6 md:pt-6 md:pb-[max(1.5rem,env(safe-area-inset-bottom))]"

View file

@ -81,10 +81,10 @@ export function PrivacyModal({ isOpen, onAccept, onDismiss }: PrivacyModalProps)
<Dialog as="div" className="relative z-[80]" onClose={onDismiss ?? (() => { })}> <Dialog as="div" className="relative z-[80]" onClose={onDismiss ?? (() => { })}>
<TransitionChild <TransitionChild
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-standard duration-slow"
enterFrom="opacity-0" enterFrom="opacity-0"
enterTo="opacity-100" enterTo="opacity-100"
leave="ease-in duration-200" leave="ease-standard duration-base"
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
@ -95,14 +95,14 @@ export function PrivacyModal({ isOpen, onAccept, onDismiss }: PrivacyModalProps)
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4"> <div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
<TransitionChild <TransitionChild
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-standard duration-slow"
enterFrom="opacity-0 scale-95" enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100" enterTo="opacity-100 scale-100"
leave="ease-in duration-200" leave="ease-standard duration-base"
leaveFrom="opacity-100 scale-100" leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95" leaveTo="opacity-0 scale-95"
> >
<DialogPanel data-testid="privacy-modal" className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition-all"> <DialogPanel data-testid="privacy-modal" className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition">
<DialogTitle <DialogTitle
as="h3" as="h3"
className="text-lg font-semibold leading-6 text-foreground" className="text-lg font-semibold leading-6 text-foreground"
@ -143,7 +143,7 @@ export function PrivacyModal({ isOpen, onAccept, onDismiss }: PrivacyModalProps)
font-medium text-background hover:bg-secondary-accent font-medium text-background hover:bg-secondary-accent
disabled:opacity-50 disabled:cursor-not-allowed disabled:opacity-50 disabled:cursor-not-allowed
focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
transform transition-transform duration-200 ease-in-out enabled:hover:scale-[1.04]" transform transition-transform duration-base ease-standard"
onClick={handleAccept} onClick={handleAccept}
> >
Continue Continue
@ -195,10 +195,10 @@ export function showPrivacyModal(): void {
<Dialog as="div" className="relative z-50" onClose={handleClose}> <Dialog as="div" className="relative z-50" onClose={handleClose}>
<TransitionChild <TransitionChild
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-standard duration-slow"
enterFrom="opacity-0" enterFrom="opacity-0"
enterTo="opacity-100" enterTo="opacity-100"
leave="ease-in duration-200" leave="ease-standard duration-base"
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
@ -209,14 +209,14 @@ export function showPrivacyModal(): void {
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4"> <div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
<TransitionChild <TransitionChild
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-standard duration-slow"
enterFrom="opacity-0 scale-95" enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100" enterTo="opacity-100 scale-100"
leave="ease-in duration-200" leave="ease-standard duration-base"
leaveFrom="opacity-100 scale-100" leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95" leaveTo="opacity-0 scale-95"
> >
<DialogPanel className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition-all"> <DialogPanel className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition">
<DialogTitle <DialogTitle
as="h3" as="h3"
className="text-lg font-semibold leading-6 text-foreground" className="text-lg font-semibold leading-6 text-foreground"
@ -232,7 +232,7 @@ export function showPrivacyModal(): void {
className="inline-flex justify-center rounded-lg bg-accent px-4 py-2 text-sm className="inline-flex justify-center rounded-lg bg-accent px-4 py-2 text-sm
font-medium text-background hover:bg-secondary-accent focus:outline-none font-medium text-background hover:bg-secondary-accent focus:outline-none
focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
transform transition-transform duration-200 ease-in-out hover:scale-[1.04]" transform transition-transform duration-base ease-standard"
onClick={handleClose} onClick={handleClose}
> >
Close Close

View file

@ -57,7 +57,7 @@ export function ProgressCard({
className={buttonClass({ className={buttonClass({
variant: 'ghost', variant: 'ghost',
size: 'xs', size: 'xs',
className: 'shrink-0 hover:bg-background/50 hover:scale-[1.04]', className: 'shrink-0 hover:bg-background/50',
})} })}
onClick={(e) => onCancel(e)} onClick={(e) => onCancel(e)}
> >
@ -68,7 +68,7 @@ export function ProgressCard({
{/* Progress bar */} {/* Progress bar */}
<div className="w-full bg-background rounded-full overflow-hidden h-1.5"> <div className="w-full bg-background rounded-full overflow-hidden h-1.5">
<div <div
className="h-full bg-accent transition-all duration-300 ease-out" className="h-full bg-accent transition duration-slow ease-standard"
style={{ width: `${progress}%` }} style={{ width: `${progress}%` }}
/> />
</div> </div>

View file

@ -32,10 +32,10 @@ export function ProgressPopup({
<Transition <Transition
show={isOpen} show={isOpen}
as={Fragment} as={Fragment}
enter="transform transition ease-out duration-300" enter="transform transition ease-standard duration-slow"
enterFrom="opacity-0 -translate-y-4" enterFrom="opacity-0 -translate-y-4"
enterTo="opacity-100 translate-y-0" enterTo="opacity-100 translate-y-0"
leave="transform transition ease-in duration-200" leave="transform transition ease-standard duration-base"
leaveFrom="opacity-100 translate-y-0" leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 -translate-y-4" leaveTo="opacity-0 -translate-y-4"
> >

View file

@ -142,11 +142,11 @@ export function SettingsTrigger({
return ( return (
<Button <Button
onClick={onOpen} onClick={onOpen}
className={`inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase hover:text-accent transition-transform transition-colors duration-200 ease-out hover:scale-[1.01] ${className}`} className={`inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase hover:text-accent transition-transform transition-colors duration-base ease-standard ${className}`}
aria-label="Settings" aria-label="Settings"
tabIndex={0} tabIndex={0}
> >
<SettingsIcon className="w-4 h-4 transition-transform duration-200 ease-out hover:scale-[1.01] hover:rotate-45" /> <SettingsIcon className="w-4 h-4 transition-transform duration-base ease-standard hover:rotate-45" />
{triggerLabel && <span className="ml-2">{triggerLabel}</span>} {triggerLabel && <span className="ml-2">{triggerLabel}</span>}
</Button> </Button>
); );
@ -519,10 +519,10 @@ export function SettingsModal({
> >
<TransitionChild <TransitionChild
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-standard duration-slow"
enterFrom="opacity-0" enterFrom="opacity-0"
enterTo="opacity-100" enterTo="opacity-100"
leave="ease-in duration-200" leave="ease-standard duration-base"
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
@ -533,14 +533,14 @@ export function SettingsModal({
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4"> <div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
<TransitionChild <TransitionChild
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-standard duration-slow"
enterFrom="opacity-0 scale-95" enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100" enterTo="opacity-100 scale-100"
leave="ease-in duration-200" leave="ease-standard duration-base"
leaveFrom="opacity-100 scale-100" leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95" leaveTo="opacity-0 scale-95"
> >
<DialogPanel data-testid="settings-modal" className="relative w-full max-w-4xl transform rounded-xl bg-base text-left align-middle shadow-xl transition-all overflow-hidden border border-offbase"> <DialogPanel data-testid="settings-modal" className="relative w-full max-w-4xl transform rounded-xl bg-base text-left align-middle shadow-xl transition overflow-hidden border border-offbase">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between px-5 py-3 border-b border-offbase"> <div className="flex items-center justify-between px-5 py-3 border-b border-offbase">
<div className="flex items-baseline gap-4"> <div className="flex items-baseline gap-4">
@ -666,7 +666,7 @@ export function SettingsModal({
</ListboxButton> </ListboxButton>
<Transition <Transition
as={Fragment} as={Fragment}
leave="transition ease-in duration-100" leave="transition ease-standard duration-fast"
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
@ -782,7 +782,7 @@ export function SettingsModal({
</ListboxButton> </ListboxButton>
<Transition <Transition
as={Fragment} as={Fragment}
leave="transition ease-in duration-100" leave="transition ease-standard duration-fast"
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
@ -908,7 +908,7 @@ export function SettingsModal({
return ( return (
<button <button
onClick={() => setTheme(systemTheme.id)} onClick={() => setTheme(systemTheme.id)}
className={`flex items-center gap-2 rounded-lg px-2 py-1.5 w-full text-left transition-all duration-200 ease-in-out transform hover:scale-[1.02] border className={`flex items-center gap-2 rounded-lg px-2 py-1.5 w-full text-left transition duration-base ease-standard transform border
${isActive ${isActive
? 'border-accent' ? 'border-accent'
: 'border-offbase hover:border-muted' : 'border-offbase hover:border-muted'
@ -952,7 +952,7 @@ export function SettingsModal({
setTheme('custom'); setTheme('custom');
setIsCustomExpanded(true); setIsCustomExpanded(true);
}} }}
className={`flex items-center gap-2 rounded-lg px-2 py-1.5 flex-1 text-left transition-all duration-200 ease-in-out transform hover:scale-[1.02] border className={`flex items-center gap-2 rounded-lg px-2 py-1.5 flex-1 text-left transition duration-base ease-standard transform border
${isActive ${isActive
? 'border-accent' ? 'border-accent'
: 'border-offbase hover:border-muted' : 'border-offbase hover:border-muted'
@ -984,7 +984,7 @@ export function SettingsModal({
style={{ color: colors.muted, backgroundColor: colors.base }} style={{ color: colors.muted, backgroundColor: colors.base }}
aria-label={isCustomExpanded ? 'Collapse color picker' : 'Expand color picker'} aria-label={isCustomExpanded ? 'Collapse color picker' : 'Expand color picker'}
> >
<svg className={`w-3.5 h-3.5 transition-transform duration-200 ${isCustomExpanded ? 'rotate-180' : ''}`} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}> <svg className={`w-3.5 h-3.5 transition-transform duration-base ${isCustomExpanded ? 'rotate-180' : ''}`} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" /> <path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
</svg> </svg>
</button> </button>
@ -1052,7 +1052,7 @@ export function SettingsModal({
<button <button
key={t.id} key={t.id}
onClick={() => setTheme(t.id)} onClick={() => setTheme(t.id)}
className={`flex items-center gap-2 rounded-lg px-2 py-1.5 text-left transition-all duration-200 ease-in-out transform hover:scale-[1.02] border className={`flex items-center gap-2 rounded-lg px-2 py-1.5 text-left transition duration-base ease-standard transform border
${isActive ${isActive
? 'border-accent' ? 'border-accent'
: 'border-offbase hover:border-muted' : 'border-offbase hover:border-muted'
@ -1094,7 +1094,7 @@ export function SettingsModal({
<button <button
key={t.id} key={t.id}
onClick={() => setTheme(t.id)} onClick={() => setTheme(t.id)}
className={`flex items-center gap-2 rounded-lg px-2 py-1.5 text-left transition-all duration-200 ease-in-out transform hover:scale-[1.02] border className={`flex items-center gap-2 rounded-lg px-2 py-1.5 text-left transition duration-base ease-standard transform border
${isActive ${isActive
? 'border-accent' ? 'border-accent'
: 'border-offbase hover:border-muted' : 'border-offbase hover:border-muted'
@ -1247,7 +1247,7 @@ export function SettingsModal({
<> <>
<Button <Button
onClick={handleSignOut} onClick={handleSignOut}
className={buttonClass({ variant: 'outline', size: 'md', className: 'hover:scale-[1.04]' })} className={buttonClass({ variant: 'outline', size: 'md' })}
> >
Disconnect account Disconnect account
</Button> </Button>
@ -1278,19 +1278,19 @@ export function SettingsModal({
</p> </p>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
<Link href="/signin"> <Link href="/signin">
<Button className={buttonClass({ variant: 'outline', size: 'md', className: 'hover:scale-[1.04]' })}> <Button className={buttonClass({ variant: 'outline', size: 'md' })}>
Connect Connect
</Button> </Button>
</Link> </Link>
{runtimeConfig.enableUserSignups && ( {runtimeConfig.enableUserSignups && (
<Link href="/signup"> <Link href="/signup">
<Button className={buttonClass({ variant: 'primary', size: 'md', className: 'hover:scale-[1.04]' })}> <Button className={buttonClass({ variant: 'primary', size: 'md' })}>
Create account Create account
</Button> </Button>
</Link> </Link>
)} )}
<Link href="/?redirect=false"> <Link href="/?redirect=false">
<Button className={buttonClass({ variant: 'outline', size: 'md', className: 'hover:scale-[1.04]' })}> <Button className={buttonClass({ variant: 'outline', size: 'md' })}>
Back to landing page Back to landing page
</Button> </Button>
</Link> </Link>
@ -1439,7 +1439,7 @@ function SettingsChangelogPanel({
<div className="flex items-center gap-3 px-4 py-3 border-b border-offbase bg-background"> <div className="flex items-center gap-3 px-4 py-3 border-b border-offbase bg-background">
<Button <Button
onClick={onClose} onClick={onClose}
className="inline-flex items-center justify-center rounded-md text-muted hover:text-accent hover:bg-base transition-all duration-200 ease-in-out transform hover:scale-[1.01]" className="inline-flex items-center justify-center rounded-md text-muted hover:text-accent hover:bg-base transition duration-base ease-standard transform"
aria-label="Back to settings" aria-label="Back to settings"
title="Back" title="Back"
> >
@ -1472,7 +1472,7 @@ function SettingsChangelogPanel({
href="https://github.com/richardr1126/openreader/releases" href="https://github.com/richardr1126/openreader/releases"
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
className="inline-flex text-xs font-medium text-accent hover:underline transition-all duration-200 ease-in-out transform hover:scale-[1.02]" className="inline-flex text-xs font-medium text-accent hover:underline transition duration-base ease-standard transform"
> >
Open GitHub Releases Open GitHub Releases
</a> </a>
@ -1497,7 +1497,7 @@ function SettingsChangelogPanel({
<button <button
type="button" type="button"
onClick={() => setExpanded((prev) => ({ ...prev, [entry.tag_name]: !isExpanded }))} onClick={() => setExpanded((prev) => ({ ...prev, [entry.tag_name]: !isExpanded }))}
className="w-full text-left py-2 flex items-center gap-2 hover:bg-base transition-all duration-200 ease-in-out transform hover:scale-[1.01]" className="w-full text-left py-2 flex items-center gap-2 hover:bg-base transition duration-base ease-standard transform"
> >
<ChevronRightIcon <ChevronRightIcon
className={`w-3.5 h-3.5 shrink-0 text-muted transition-transform ${ className={`w-3.5 h-3.5 shrink-0 text-muted transition-transform ${
@ -1542,7 +1542,7 @@ function SettingsChangelogPanel({
href={entry.html_url} href={entry.html_url}
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
className="inline-flex text-xs font-medium text-accent hover:underline transition-all duration-200 ease-in-out transform hover:scale-[1.02]" className="inline-flex text-xs font-medium text-accent hover:underline transition duration-base ease-standard transform"
> >
View on GitHub View on GitHub
</a> </a>

View file

@ -78,10 +78,10 @@ export default function ClaimDataModal({
<Dialog as="div" className="relative z-[80]" onClose={onDismiss}> <Dialog as="div" className="relative z-[80]" onClose={onDismiss}>
<TransitionChild <TransitionChild
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-standard duration-slow"
enterFrom="opacity-0" enterFrom="opacity-0"
enterTo="opacity-100" enterTo="opacity-100"
leave="ease-in duration-200" leave="ease-standard duration-base"
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
@ -92,14 +92,14 @@ export default function ClaimDataModal({
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4"> <div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
<TransitionChild <TransitionChild
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-standard duration-slow"
enterFrom="opacity-0 scale-95" enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100" enterTo="opacity-100 scale-100"
leave="ease-in duration-200" leave="ease-standard duration-base"
leaveFrom="opacity-100 scale-100" leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95" leaveTo="opacity-0 scale-95"
> >
<DialogPanel data-testid="claim-modal" className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition-all"> <DialogPanel data-testid="claim-modal" className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition">
<DialogTitle <DialogTitle
as="h3" as="h3"
className="text-lg font-semibold leading-6 text-foreground mb-4" className="text-lg font-semibold leading-6 text-foreground mb-4"
@ -135,7 +135,7 @@ export default function ClaimDataModal({
className="inline-flex justify-center rounded-lg bg-background px-3 py-1.5 text-sm className="inline-flex justify-center rounded-lg bg-background px-3 py-1.5 text-sm
font-medium text-foreground hover:bg-offbase focus:outline-none font-medium text-foreground hover:bg-offbase focus:outline-none
focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-accent transform transition-transform duration-base ease-standard hover:text-accent
disabled:opacity-50" disabled:opacity-50"
> >
Dismiss Dismiss
@ -148,7 +148,7 @@ export default function ClaimDataModal({
className="inline-flex justify-center rounded-lg bg-accent px-3 py-1.5 text-sm className="inline-flex justify-center rounded-lg bg-accent px-3 py-1.5 text-sm
font-medium text-background hover:bg-secondary-accent focus:outline-none font-medium text-background hover:bg-secondary-accent focus:outline-none
focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-background transform transition-transform duration-base ease-standard hover:text-background
disabled:opacity-50" disabled:opacity-50"
> >
{isClaiming ? 'Claiming...' : 'Claim Data'} {isClaiming ? 'Claiming...' : 'Claim Data'}

View file

@ -36,7 +36,7 @@ export function RateLimitBanner({ className = '' }: RateLimitBannerProps) {
href="/signup" href="/signup"
className="inline-flex items-center px-2 py-1 text-xs font-medium rounded-md className="inline-flex items-center px-2 py-1 text-xs font-medium rounded-md
bg-accent text-background hover:bg-secondary-accent bg-accent text-background hover:bg-secondary-accent
transform transition-transform duration-200 hover:scale-[1.04]" transform transition-transform duration-base"
> >
Sign up for a higher limit Sign up for a higher limit
</Link> </Link>

View file

@ -32,7 +32,7 @@ export function UserMenu({
}; };
const rowClass = const rowClass =
'w-full inline-flex items-center gap-2 px-2 py-1 rounded-md text-[12px] border border-transparent transition-all duration-200 ease-out text-left hover:scale-[1.01] hover:border-accent hover:bg-offbase hover:text-accent'; 'w-full inline-flex items-center gap-2 px-2 py-1 rounded-md text-[12px] border border-transparent transition duration-base ease-standard text-left hover:border-accent hover:bg-offbase hover:text-accent';
if (!session || session.user.isAnonymous) { if (!session || session.user.isAnonymous) {
if (variant === 'sidebar') { if (variant === 'sidebar') {
@ -55,13 +55,13 @@ export function UserMenu({
return ( return (
<div className={`flex gap-2 ${className}`}> <div className={`flex gap-2 ${className}`}>
<Link href="/signin"> <Link href="/signin">
<Button className="inline-flex items-center rounded-md bg-base border border-offbase px-2 py-1 text-xs font-medium text-foreground hover:bg-offbase focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-2 transform transition-all duration-200 ease-in-out hover:scale-[1.01] hover:text-accent"> <Button className="inline-flex items-center rounded-md bg-base border border-offbase px-2 py-1 text-xs font-medium text-foreground hover:bg-offbase focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-2 transform transition duration-base ease-standard hover:text-accent">
Connect Connect
</Button> </Button>
</Link> </Link>
{enableUserSignups && ( {enableUserSignups && (
<Link href="/signup"> <Link href="/signup">
<Button className="inline-flex items-center rounded-md bg-accent px-2 py-1 text-xs font-medium text-background hover:bg-secondary-accent focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-2 transform transition-all duration-200 ease-in-out hover:scale-[1.01]"> <Button className="inline-flex items-center rounded-md bg-accent px-2 py-1 text-xs font-medium text-background hover:bg-secondary-accent focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-2 transform transition duration-base ease-standard">
Create account Create account
</Button> </Button>
</Link> </Link>
@ -97,7 +97,7 @@ export function UserMenu({
<Button <Button
onClick={handleDisconnectAccount} onClick={handleDisconnectAccount}
className="inline-flex items-center text-foreground text-xs hover:text-accent transform transition-all duration-200 ease-in-out hover:scale-[1.01]" className="inline-flex items-center text-foreground text-xs hover:text-accent transform transition duration-base ease-standard"
title="Disconnect account" title="Disconnect account"
> >
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">

View file

@ -21,10 +21,10 @@ export function CreateFolderDialog({
<Dialog as="div" className="relative z-50" onClose={onClose}> <Dialog as="div" className="relative z-50" onClose={onClose}>
<TransitionChild <TransitionChild
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-standard duration-slow"
enterFrom="opacity-0" enterFrom="opacity-0"
enterTo="opacity-100" enterTo="opacity-100"
leave="ease-in duration-200" leave="ease-standard duration-base"
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
@ -35,14 +35,14 @@ export function CreateFolderDialog({
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4"> <div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
<TransitionChild <TransitionChild
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-standard duration-slow"
enterFrom="opacity-0 scale-95" enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100" enterTo="opacity-100 scale-100"
leave="ease-in duration-200" leave="ease-standard duration-base"
leaveFrom="opacity-100 scale-100" leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95" leaveTo="opacity-0 scale-95"
> >
<DialogPanel className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition-all"> <DialogPanel className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition">
<DialogTitle as="h3" className="text-lg font-semibold text-foreground"> <DialogTitle as="h3" className="text-lg font-semibold text-foreground">
Create New Folder Create New Folder
</DialogTitle> </DialogTitle>

View file

@ -168,7 +168,7 @@ function SidebarUploadLoader({
strokeDasharray={`${circumference} ${circumference}`} strokeDasharray={`${circumference} ${circumference}`}
strokeDashoffset={dashOffset} strokeDashoffset={dashOffset}
transform={`rotate(-90 ${size / 2} ${size / 2})`} transform={`rotate(-90 ${size / 2} ${size / 2})`}
style={{ transition: 'stroke-dashoffset 200ms ease-out' }} style={{ transition: 'stroke-dashoffset 200ms ease-standard' }}
/> />
</svg> </svg>
</div> </div>
@ -657,7 +657,7 @@ function DocumentListInner({ brand, appActions }: DocumentListInnerProps) {
<button <button
type="button" type="button"
onClick={() => setShowHint(false)} onClick={() => setShowHint(false)}
className="h-6 w-6 inline-flex items-center justify-center text-muted hover:text-accent hover:bg-base hover:scale-[1.01] rounded transition-all duration-200 ease-out" className="h-6 w-6 inline-flex items-center justify-center text-muted hover:text-accent hover:bg-base rounded transition duration-base ease-standard"
aria-label="Dismiss hint" aria-label="Dismiss hint"
> >
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">

View file

@ -261,7 +261,7 @@ export function DocumentPreview({ doc }: DocumentPreviewProps) {
ref={imageRef} ref={imageRef}
src={imagePreview} src={imagePreview}
alt={`${doc.name} preview`} alt={`${doc.name} preview`}
className={`absolute inset-0 h-full w-full object-cover transition-opacity duration-150 ${isImageReady ? 'opacity-100' : 'opacity-0'}`} className={`absolute inset-0 h-full w-full object-cover transition-opacity duration-fast ${isImageReady ? 'opacity-100' : 'opacity-0'}`}
draggable={false} draggable={false}
loading="lazy" loading="lazy"
onLoad={() => { onLoad={() => {

View file

@ -138,7 +138,7 @@ export function DocumentTile({
data-doc-tile data-doc-tile
aria-selected={isSelected} aria-selected={isSelected}
className={ className={
'group relative flex flex-col rounded-md overflow-hidden border transition-all duration-200 ease-out hover:scale-[1.01] ' + 'group relative flex flex-col rounded-md overflow-hidden border transition duration-base ease-standard ' +
(isSelected (isSelected
? 'border-accent bg-offbase' ? 'border-accent bg-offbase'
: 'border-offbase bg-base hover:bg-offbase hover:border-accent') + : 'border-offbase bg-base hover:bg-offbase hover:border-accent') +
@ -187,7 +187,7 @@ export function DocumentTile({
{showDeleteButton && ( {showDeleteButton && (
<Button <Button
onClick={() => onDelete(doc)} onClick={() => onDelete(doc)}
className={`inline-flex items-center justify-center text-muted hover:text-accent hover:bg-base focus:outline-none transition-colors duration-200 ${TRASH_BTN_CLASSES[iconSize]}`} className={`inline-flex items-center justify-center text-muted hover:text-accent hover:bg-base focus:outline-none transition-colors duration-base ${TRASH_BTN_CLASSES[iconSize]}`}
aria-label={`Delete ${doc.name}`} aria-label={`Delete ${doc.name}`}
> >
<svg className={TRASH_ICON_CLASSES[iconSize]} fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className={TRASH_ICON_CLASSES[iconSize]} fill="none" stroke="currentColor" viewBox="0 0 24 24">

View file

@ -92,7 +92,7 @@ function GalleryThumb({
onClick={onClick} onClick={onClick}
aria-current={active ? 'true' : undefined} aria-current={active ? 'true' : undefined}
className={ className={
'group relative w-[98px] sm:w-[110px] shrink-0 cursor-pointer rounded-lg overflow-hidden border bg-base snap-start transition-all duration-200 ease-out ' + 'group relative w-[98px] sm:w-[110px] shrink-0 cursor-pointer rounded-lg overflow-hidden border bg-base snap-start transition duration-base ease-standard ' +
(active (active
? 'border-accent shadow-[0_10px_24px_-18px_rgba(0,0,0,0.85)] -translate-y-0.5' ? 'border-accent shadow-[0_10px_24px_-18px_rgba(0,0,0,0.85)] -translate-y-0.5'
: 'border-offbase hover:border-accent hover:-translate-y-0.5') + : 'border-offbase hover:border-accent hover:-translate-y-0.5') +
@ -106,20 +106,20 @@ function GalleryThumb({
</div> </div>
<div <div
className={ className={
'px-2 py-1.5 flex items-center gap-1.5 border-t transition-colors duration-200 ' + 'px-2 py-1.5 flex items-center gap-1.5 border-t transition-colors duration-base ' +
(active ? 'bg-offbase border-accent' : 'bg-base border-offbase') (active ? 'bg-offbase border-accent' : 'bg-base border-offbase')
} }
> >
<KindIcon <KindIcon
doc={doc} doc={doc}
className={ className={
'w-3 h-3 shrink-0 transition-colors duration-200 ' + 'w-3 h-3 shrink-0 transition-colors duration-base ' +
(active ? 'text-accent' : 'text-muted') (active ? 'text-accent' : 'text-muted')
} }
/> />
<span <span
className={ className={
'truncate text-[10px] leading-none transition-colors duration-200 ' + 'truncate text-[10px] leading-none transition-colors duration-base ' +
(active ? 'text-accent font-medium' : 'text-foreground') (active ? 'text-accent font-medium' : 'text-foreground')
} }
> >

View file

@ -62,7 +62,7 @@ function HeaderCell({
onSortChange(field, nextDir); onSortChange(field, nextDir);
}} }}
className={ className={
'flex items-center gap-1 px-2 py-1.5 text-[10px] uppercase tracking-wide font-semibold transition-colors duration-200 ease-out hover:text-accent ' + 'flex items-center gap-1 px-2 py-1.5 text-[10px] uppercase tracking-wide font-semibold transition-colors duration-base ease-standard hover:text-accent ' +
(active ? 'text-accent' : 'text-muted') + (active ? 'text-accent' : 'text-muted') +
(align === 'right' ? ' justify-end' : '') + (align === 'right' ? ' justify-end' : '') +
' ' + ' ' +
@ -131,7 +131,7 @@ function DocRow({
data-doc-tile data-doc-tile
aria-selected={isSelected} aria-selected={isSelected}
className={ className={
'grid grid-cols-[minmax(0,1fr)_44px_72px_104px_28px] sm:grid-cols-[minmax(0,1fr)_56px_96px_140px_32px] items-center text-[12px] border-b border-offbase transition-colors duration-200 ease-out ' + 'grid grid-cols-[minmax(0,1fr)_44px_72px_104px_28px] sm:grid-cols-[minmax(0,1fr)_56px_96px_140px_32px] items-center text-[12px] border-b border-offbase transition-colors duration-base ease-standard ' +
(isSelected (isSelected
? 'bg-offbase text-accent' ? 'bg-offbase text-accent'
: 'text-foreground hover:bg-offbase') + : 'text-foreground hover:bg-offbase') +
@ -161,7 +161,7 @@ function DocRow({
e.stopPropagation(); e.stopPropagation();
onDeleteDoc(doc); onDeleteDoc(doc);
}} }}
className="h-7 w-7 flex items-center justify-center text-muted hover:text-accent hover:bg-offbase hover:scale-[1.02] rounded transition-all duration-200 ease-out" className="h-7 w-7 flex items-center justify-center text-muted hover:text-accent hover:bg-offbase rounded transition duration-base ease-standard"
aria-label={`Delete ${doc.name}`} aria-label={`Delete ${doc.name}`}
> >
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">

View file

@ -56,7 +56,7 @@ function SidebarRow({
type="button" type="button"
onClick={onClick} onClick={onClick}
className={ className={
'group w-full flex items-center gap-2 px-2 py-1 rounded-md text-[12px] border transform transition-all duration-200 ease-out text-left hover:scale-[1.01] ' + 'group w-full flex items-center gap-2 px-2 py-1 rounded-md text-[12px] border transform transition duration-base ease-standard text-left ' +
(active (active
? 'border-accent bg-offbase text-accent' ? 'border-accent bg-offbase text-accent'
: 'border-transparent bg-transparent text-foreground hover:border-accent hover:text-accent') + : 'border-transparent bg-transparent text-foreground hover:border-accent hover:text-accent') +
@ -65,7 +65,7 @@ function SidebarRow({
> >
<span <span
className={ className={
'w-4 h-4 shrink-0 flex items-center justify-center transition-colors duration-200 ' + 'w-4 h-4 shrink-0 flex items-center justify-center transition-colors duration-base ' +
(active ? 'text-accent' : 'text-muted group-hover:text-accent') (active ? 'text-accent' : 'text-muted group-hover:text-accent')
} }
> >
@ -74,7 +74,7 @@ function SidebarRow({
<span className="truncate flex-1">{label}</span> <span className="truncate flex-1">{label}</span>
{typeof count === 'number' && count > 0 && ( {typeof count === 'number' && count > 0 && (
<span <span
className={`text-[10px] text-muted tabular-nums transition-transform duration-200 ease-out ${countClassName ?? ''}`} className={`text-[10px] text-muted tabular-nums transition-transform duration-base ease-standard ${countClassName ?? ''}`}
> >
{count} {count}
</span> </span>
@ -272,7 +272,7 @@ export function FinderSidebar({
rightSlot={( rightSlot={(
<Menu as="div" className="relative inline-flex items-center leading-none text-left shrink-0 normal-case tracking-normal font-normal"> <Menu as="div" className="relative inline-flex items-center leading-none text-left shrink-0 normal-case tracking-normal font-normal">
<MenuButton <MenuButton
className="inline-flex items-center justify-center h-3.5 w-5 rounded-sm text-muted hover:text-accent transition-colors duration-200 ease-out focus:outline-none" className="inline-flex items-center justify-center h-3.5 w-5 rounded-sm text-muted hover:text-accent transition-colors duration-base ease-standard focus:outline-none"
title="Folder actions" title="Folder actions"
aria-label="Folder actions" aria-label="Folder actions"
> >
@ -280,10 +280,10 @@ export function FinderSidebar({
</MenuButton> </MenuButton>
<Transition <Transition
as={Fragment} as={Fragment}
enter="transition ease-out duration-100" enter="transition ease-standard duration-fast"
enterFrom="transform opacity-0 scale-95" enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100" enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75" leave="transition ease-standard duration-fast"
leaveFrom="transform opacity-100 scale-100" leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95" leaveTo="transform opacity-0 scale-95"
> >
@ -370,7 +370,7 @@ export function FinderSidebar({
onPointerMove={onResizeMove} onPointerMove={onResizeMove}
onPointerUp={onResizeEnd} onPointerUp={onResizeEnd}
onPointerCancel={onResizeEnd} onPointerCancel={onResizeEnd}
className="hidden md:block absolute top-0 right-0 h-full w-1 cursor-col-resize hover:bg-offbase active:bg-accent transition-colors duration-200 ease-out" className="hidden md:block absolute top-0 right-0 h-full w-1 cursor-col-resize hover:bg-offbase active:bg-accent transition-colors duration-base ease-standard"
/> />
</aside> </aside>
); );

View file

@ -54,7 +54,7 @@ const ICON_SIZES: Array<{ value: IconSize; label: string }> = [
// Match SettingsModal / UserMenu trigger sizing exactly so all bar buttons share one rhythm. // Match SettingsModal / UserMenu trigger sizing exactly so all bar buttons share one rhythm.
const TOOLBAR_BTN = const TOOLBAR_BTN =
'inline-flex items-center py-1 px-2 rounded-md border bg-base text-xs transition-all duration-200 ease-out hover:scale-[1.01]'; 'inline-flex items-center py-1 px-2 rounded-md border bg-base text-xs transition duration-base ease-standard';
const TOOLBAR_BTN_INACTIVE = const TOOLBAR_BTN_INACTIVE =
'border-offbase text-foreground hover:text-accent hover:border-accent hover:bg-offbase'; 'border-offbase text-foreground hover:text-accent hover:border-accent hover:bg-offbase';
const TOOLBAR_BTN_ACTIVE = 'border-accent bg-offbase text-accent'; const TOOLBAR_BTN_ACTIVE = 'border-accent bg-offbase text-accent';
@ -64,7 +64,7 @@ const TOOLBAR_BTN_ACTIVE = 'border-accent bg-offbase text-accent';
// matches the height of a standalone TOOLBAR_BTN. // matches the height of a standalone TOOLBAR_BTN.
const PILL = 'inline-flex items-center rounded-md border border-offbase bg-base p-0.5 gap-0.5 shrink-0'; const PILL = 'inline-flex items-center rounded-md border border-offbase bg-base p-0.5 gap-0.5 shrink-0';
const PILL_SEGMENT = const PILL_SEGMENT =
'inline-flex items-center justify-center rounded-[5px] text-xs transition-colors duration-200 ease-out'; 'inline-flex items-center justify-center rounded-[5px] text-xs transition-colors duration-base ease-standard';
const PILL_SEGMENT_INACTIVE = 'text-muted hover:bg-offbase hover:text-accent'; const PILL_SEGMENT_INACTIVE = 'text-muted hover:bg-offbase hover:text-accent';
const PILL_SEGMENT_ACTIVE = 'bg-offbase text-accent'; const PILL_SEGMENT_ACTIVE = 'bg-offbase text-accent';
@ -133,7 +133,7 @@ export function FinderToolbar({
</button> </button>
{isIconsToggle && viewMode === 'icons' && ( {isIconsToggle && viewMode === 'icons' && (
<div <div
className="absolute top-full left-1/2 z-30 -translate-x-1/2 pt-1 opacity-0 pointer-events-none transition-opacity duration-150 group-hover/icons:opacity-100 group-hover/icons:pointer-events-auto group-focus-within/icons:opacity-100 group-focus-within/icons:pointer-events-auto" className="absolute top-full left-1/2 z-30 -translate-x-1/2 pt-1 opacity-0 pointer-events-none transition-opacity duration-fast group-hover/icons:opacity-100 group-hover/icons:pointer-events-auto group-focus-within/icons:opacity-100 group-focus-within/icons:pointer-events-auto"
> >
<div className={`${PILL} shadow-lg`}> <div className={`${PILL} shadow-lg`}>
{ICON_SIZES.map(({ value: sizeValue, label: sizeLabel }) => { {ICON_SIZES.map(({ value: sizeValue, label: sizeLabel }) => {
@ -204,7 +204,7 @@ export function FinderToolbar({
<div className="flex-1 min-w-0" /> <div className="flex-1 min-w-0" />
<div className="hidden sm:flex items-center gap-1.5 px-2 py-1 rounded-md bg-background border border-offbase hover:border-accent focus-within:ring-1 focus-within:ring-accent focus-within:border-accent transition-colors duration-200 ease-out w-[160px] md:w-[200px]"> <div className="hidden sm:flex items-center gap-1.5 px-2 py-1 rounded-md bg-background border border-offbase hover:border-accent focus-within:ring-1 focus-within:ring-accent focus-within:border-accent transition-colors duration-base ease-standard w-[160px] md:w-[200px]">
<SearchIcon className="w-3.5 h-3.5 text-muted shrink-0" /> <SearchIcon className="w-3.5 h-3.5 text-muted shrink-0" />
<input <input
type="search" type="search"

View file

@ -69,10 +69,10 @@ export function FinderWindow({
> >
<TransitionChild <TransitionChild
as={Fragment} as={Fragment}
enter="transition-opacity duration-150" enter="transition-opacity duration-fast"
enterFrom="opacity-0" enterFrom="opacity-0"
enterTo="opacity-100" enterTo="opacity-100"
leave="transition-opacity duration-100" leave="transition-opacity duration-fast"
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
@ -81,10 +81,10 @@ export function FinderWindow({
<div className="fixed inset-y-0 left-0 flex max-w-full"> <div className="fixed inset-y-0 left-0 flex max-w-full">
<TransitionChild <TransitionChild
as={Fragment} as={Fragment}
enter="transition-transform duration-200 ease-out" enter="transition-transform duration-base ease-standard"
enterFrom="-translate-x-full" enterFrom="-translate-x-full"
enterTo="translate-x-0" enterTo="translate-x-0"
leave="transition-transform duration-150 ease-in" leave="transition-transform duration-fast ease-standard"
leaveFrom="translate-x-0" leaveFrom="translate-x-0"
leaveTo="-translate-x-full" leaveTo="-translate-x-full"
> >

View file

@ -140,10 +140,10 @@ export function DexieMigrationModal({
<Dialog as="div" className="relative z-[80]" onClose={() => (closeDisabled ? null : onComplete())}> <Dialog as="div" className="relative z-[80]" onClose={() => (closeDisabled ? null : onComplete())}>
<TransitionChild <TransitionChild
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-standard duration-slow"
enterFrom="opacity-0" enterFrom="opacity-0"
enterTo="opacity-100" enterTo="opacity-100"
leave="ease-in duration-200" leave="ease-standard duration-base"
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
@ -154,14 +154,14 @@ export function DexieMigrationModal({
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4"> <div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
<TransitionChild <TransitionChild
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-standard duration-slow"
enterFrom="opacity-0 scale-95" enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100" enterTo="opacity-100 scale-100"
leave="ease-in duration-200" leave="ease-standard duration-base"
leaveFrom="opacity-100 scale-100" leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95" leaveTo="opacity-0 scale-95"
> >
<DialogPanel data-testid="migration-modal" className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition-all"> <DialogPanel data-testid="migration-modal" className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition">
<DialogTitle as="h3" className="text-lg font-semibold leading-6 text-foreground mb-4"> <DialogTitle as="h3" className="text-lg font-semibold leading-6 text-foreground mb-4">
{title} {title}
</DialogTitle> </DialogTitle>
@ -192,7 +192,7 @@ export function DexieMigrationModal({
className="inline-flex justify-center rounded-lg bg-background px-3 py-1.5 text-sm className="inline-flex justify-center rounded-lg bg-background px-3 py-1.5 text-sm
font-medium text-foreground hover:bg-offbase focus:outline-none font-medium text-foreground hover:bg-offbase focus:outline-none
focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-accent transform transition-transform duration-base ease-standard hover:text-accent
disabled:opacity-50" disabled:opacity-50"
> >
Skip Skip
@ -203,7 +203,7 @@ export function DexieMigrationModal({
className="inline-flex justify-center rounded-lg bg-accent px-3 py-1.5 text-sm className="inline-flex justify-center rounded-lg bg-accent px-3 py-1.5 text-sm
font-medium text-background hover:bg-secondary-accent focus:outline-none font-medium text-background hover:bg-secondary-accent focus:outline-none
focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-background transform transition-transform duration-base ease-standard hover:text-background
disabled:opacity-50" disabled:opacity-50"
> >
{isUploading ? 'Uploading…' : 'Upload'} {isUploading ? 'Uploading…' : 'Upload'}

View file

@ -49,7 +49,7 @@ export function DocumentHeaderMenu({
{onOpenSegments && ( {onOpenSegments && (
<button <button
onClick={onOpenSegments} onClick={onOpenSegments}
className={`inline-flex items-center py-1 px-2 rounded-md border bg-base text-xs transition-all duration-200 ease-in-out hover:scale-[1.09] ${ className={`inline-flex items-center py-1 px-2 rounded-md border bg-base text-xs transition duration-base ease-standard ${
isSegmentsOpen isSegmentsOpen
? 'border-accent text-accent bg-offbase' ? 'border-accent text-accent bg-offbase'
: 'border-offbase text-foreground hover:bg-offbase hover:text-accent' : 'border-offbase text-foreground hover:bg-offbase hover:text-accent'
@ -57,13 +57,13 @@ export function DocumentHeaderMenu({
aria-label={isSegmentsOpen ? 'Hide segments sidebar' : 'Open segments sidebar'} aria-label={isSegmentsOpen ? 'Hide segments sidebar' : 'Open segments sidebar'}
title={isSegmentsOpen ? 'Hide Segments' : 'Segments'} title={isSegmentsOpen ? 'Hide Segments' : 'Segments'}
> >
<ListIcon className="w-4 h-4 transform transition-transform duration-200 ease-in-out hover:scale-[1.09]" /> <ListIcon className="w-4 h-4 transform transition-transform duration-base ease-standard" />
</button> </button>
)} )}
{showAudiobookExport && onOpenAudiobook && ( {showAudiobookExport && onOpenAudiobook && (
<button <button
onClick={onOpenAudiobook} onClick={onOpenAudiobook}
className={`inline-flex items-center py-1 px-2 rounded-md border bg-base text-xs transition-all duration-200 ease-in-out hover:scale-[1.09] ${ className={`inline-flex items-center py-1 px-2 rounded-md border bg-base text-xs transition duration-base ease-standard ${
isAudiobookOpen isAudiobookOpen
? 'border-accent text-accent bg-offbase' ? 'border-accent text-accent bg-offbase'
: 'border-offbase text-foreground hover:bg-offbase hover:text-accent' : 'border-offbase text-foreground hover:bg-offbase hover:text-accent'
@ -71,12 +71,12 @@ export function DocumentHeaderMenu({
aria-label={isAudiobookOpen ? 'Hide audiobook export' : 'Open audiobook export'} aria-label={isAudiobookOpen ? 'Hide audiobook export' : 'Open audiobook export'}
title={isAudiobookOpen ? 'Hide Export Audiobook' : 'Export Audiobook'} title={isAudiobookOpen ? 'Hide Export Audiobook' : 'Export Audiobook'}
> >
<DownloadIcon className="w-4 h-4 transform transition-transform duration-200 ease-in-out hover:scale-[1.09]" /> <DownloadIcon className="w-4 h-4 transform transition-transform duration-base ease-standard" />
</button> </button>
)} )}
<button <button
onClick={onOpenSettings} onClick={onOpenSettings}
className={`inline-flex items-center py-1 px-2 rounded-md border bg-base text-xs transition-all duration-200 ease-in-out hover:scale-[1.09] ${ className={`inline-flex items-center py-1 px-2 rounded-md border bg-base text-xs transition duration-base ease-standard ${
isSettingsOpen isSettingsOpen
? 'border-accent text-accent bg-offbase' ? 'border-accent text-accent bg-offbase'
: 'border-offbase text-foreground hover:bg-offbase hover:text-accent' : 'border-offbase text-foreground hover:bg-offbase hover:text-accent'
@ -84,7 +84,7 @@ export function DocumentHeaderMenu({
aria-label={isSettingsOpen ? 'Hide settings' : 'Open settings'} aria-label={isSettingsOpen ? 'Hide settings' : 'Open settings'}
title={isSettingsOpen ? 'Hide Settings' : 'Settings'} title={isSettingsOpen ? 'Hide Settings' : 'Settings'}
> >
<FileSettingsIcon className="w-4 h-4 transform transition-transform duration-200 ease-in-out hover:scale-[1.09]" /> <FileSettingsIcon className="w-4 h-4 transform transition-transform duration-base ease-standard" />
</button> </button>
<UserMenu /> <UserMenu />
</div> </div>
@ -95,17 +95,17 @@ export function DocumentHeaderMenu({
<div className="sm:hidden flex items-center"> <div className="sm:hidden flex items-center">
<Menu as="div" className="relative inline-block text-left"> <Menu as="div" className="relative inline-block text-left">
<MenuButton <MenuButton
className="inline-flex items-center justify-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition-all duration-200 ease-in-out hover:scale-[1.09] hover:text-accent focus:outline-none focus-visible:ring-2 focus-visible:ring-accent" className="inline-flex items-center justify-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition duration-base ease-standard hover:text-accent focus:outline-none focus-visible:ring-2 focus-visible:ring-accent"
title="Menu" title="Menu"
> >
<DotsVerticalIcon className="w-4 h-4 transform transition-transform duration-200 ease-in-out hover:scale-[1.09] hover:text-accent" /> <DotsVerticalIcon className="w-4 h-4 transform transition-transform duration-base ease-standard hover:text-accent" />
</MenuButton> </MenuButton>
<Transition <Transition
as={Fragment} as={Fragment}
enter="transition ease-out duration-100" enter="transition ease-standard duration-fast"
enterFrom="transform opacity-0 scale-95" enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100" enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75" leave="transition ease-standard duration-fast"
leaveFrom="transform opacity-100 scale-100" leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95" leaveTo="transform opacity-0 scale-95"
> >

View file

@ -118,10 +118,10 @@ export function DocumentSelectionModal({
<Dialog as="div" className="relative z-[60]" onClose={onClose}> <Dialog as="div" className="relative z-[60]" onClose={onClose}>
<TransitionChild <TransitionChild
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-standard duration-slow"
enterFrom="opacity-0" enterFrom="opacity-0"
enterTo="opacity-100" enterTo="opacity-100"
leave="ease-in duration-200" leave="ease-standard duration-base"
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
@ -132,14 +132,14 @@ export function DocumentSelectionModal({
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4"> <div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
<TransitionChild <TransitionChild
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-standard duration-slow"
enterFrom="opacity-0 scale-95" enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100" enterTo="opacity-100 scale-100"
leave="ease-in duration-200" leave="ease-standard duration-base"
leaveFrom="opacity-100 scale-100" leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95" leaveTo="opacity-0 scale-95"
> >
<DialogPanel className="w-full max-w-2xl transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition-all flex flex-col h-[80vh]"> <DialogPanel className="w-full max-w-2xl transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition flex flex-col h-[80vh]">
<DialogTitle <DialogTitle
as="h3" as="h3"
className="text-lg font-semibold leading-6 text-foreground mb-4 flex-shrink-0 flex justify-between items-center" className="text-lg font-semibold leading-6 text-foreground mb-4 flex-shrink-0 flex justify-between items-center"

View file

@ -155,9 +155,7 @@ export function DocumentUploader({
noKeyboard: variant === 'overlay' noKeyboard: variant === 'overlay'
}); });
const containerBase = `group w-full rounded transform transition-all duration-200 ease-in-out ${ const containerBase = `group w-full rounded transition duration-base ease-standard ${
variant === 'compact' ? 'hover:scale-[1.01]' : ''
} ${
isUploading || isConverting ? 'cursor-not-allowed opacity-50' : 'cursor-pointer' isUploading || isConverting ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'
} ${className}`; } ${className}`;
@ -171,7 +169,7 @@ export function DocumentUploader({
: `${ : `${
isDragActive isDragActive
? 'border-2 border-dashed border-accent bg-base text-foreground' ? 'border-2 border-dashed border-accent bg-base text-foreground'
: 'border-2 border-dashed border-muted bg-transparent text-foreground hover:border-accent hover:bg-base hover:scale-[1.01]' : 'border-2 border-dashed border-muted bg-transparent text-foreground hover:border-accent hover:bg-base'
}`; }`;
const paddingClass = variant === 'compact' ? 'py-1 px-2 rounded-md' : 'py-5 px-3 rounded-lg'; const paddingClass = variant === 'compact' ? 'py-1 px-2 rounded-md' : 'py-5 px-3 rounded-lg';
@ -219,7 +217,7 @@ export function DocumentUploader({
<input {...getInputProps()} /> <input {...getInputProps()} />
{variant === 'compact' ? ( {variant === 'compact' ? (
<div className="flex items-center gap-2 text-left w-full min-w-0"> <div className="flex items-center gap-2 text-left w-full min-w-0">
<UploadIcon className="w-3.5 h-3.5 text-muted group-hover:text-accent shrink-0 transition-colors duration-200" /> <UploadIcon className="w-3.5 h-3.5 text-muted group-hover:text-accent shrink-0 transition-colors duration-base" />
{isUploading ? ( {isUploading ? (
<p className="text-[12px] font-medium truncate flex-1">Uploading</p> <p className="text-[12px] font-medium truncate flex-1">Uploading</p>
) : isConverting ? ( ) : isConverting ? (

View file

@ -22,7 +22,7 @@ export function ZoomControl({
className={buttonClass({ className={buttonClass({
variant: 'ghost', variant: 'ghost',
size: 'icon', size: 'icon',
className: 'h-6 w-6 text-sm leading-none hover:scale-[1.09]', className: 'h-6 w-6 text-sm leading-none',
})} })}
aria-label="Zoom out" aria-label="Zoom out"
> >
@ -36,7 +36,7 @@ export function ZoomControl({
className={buttonClass({ className={buttonClass({
variant: 'ghost', variant: 'ghost',
size: 'icon', size: 'icon',
className: 'h-6 w-6 text-sm leading-none hover:scale-[1.09]', className: 'h-6 w-6 text-sm leading-none',
})} })}
aria-label="Zoom in" aria-label="Zoom in"
> >

View file

@ -39,7 +39,7 @@ export const inputClass =
'w-full rounded-md bg-background border border-offbase px-2.5 py-1.5 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent'; 'w-full rounded-md bg-background border border-offbase px-2.5 py-1.5 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent';
export const listboxButtonClass = export const listboxButtonClass =
'relative w-full cursor-pointer rounded-md bg-background border border-offbase py-1.5 pl-2.5 pr-9 text-left text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent hover:bg-base transition-transform duration-200 ease-out hover:scale-[1.01]'; 'relative w-full cursor-pointer rounded-md bg-background border border-offbase py-1.5 pl-2.5 pr-9 text-left text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent hover:bg-base transition-colors duration-fast ease-standard';
export const listboxOptionsClass = export const listboxOptionsClass =
'z-50 w-[var(--button-width)] max-h-60 overflow-y-auto overscroll-contain rounded-md bg-background p-1 shadow-lg ring-1 ring-offbase focus:outline-none [--anchor-gap:0.25rem]'; 'z-50 w-[var(--button-width)] max-h-60 overflow-y-auto overscroll-contain rounded-md bg-background p-1 shadow-lg ring-1 ring-offbase focus:outline-none [--anchor-gap:0.25rem]';
export const listboxOptionClass = (active: boolean) => export const listboxOptionClass = (active: boolean) =>
@ -48,10 +48,10 @@ export const listboxOptionClass = (active: boolean) =>
export const segmentedGroupClass = export const segmentedGroupClass =
'grid gap-1 rounded-full border border-offbase bg-background p-1'; 'grid gap-1 rounded-full border border-offbase bg-background p-1';
export const segmentedButtonClass = (active: boolean) => export const segmentedButtonClass = (active: boolean) =>
`rounded-full px-2.5 py-1.5 text-xs font-medium transition-colors transition-transform duration-200 ease-out focus:outline-none focus-visible:ring-2 focus-visible:ring-accent ${ `rounded-full px-2.5 py-1.5 text-xs font-medium transition-colors duration-fast ease-standard focus:outline-none focus-visible:ring-2 focus-visible:ring-accent ${
active active
? 'bg-accent text-background hover:scale-[1.01]' ? 'bg-accent text-background'
: 'text-muted hover:bg-base hover:text-foreground hover:scale-[1.02]' : 'text-muted hover:bg-base hover:text-foreground'
}`; }`;
export function Section({ export function Section({
@ -108,7 +108,7 @@ export function Card({
className?: string; className?: string;
}) { }) {
return ( return (
<div className={`rounded-lg border border-offbase bg-background px-3 py-2 transition-transform duration-200 ease-out hover:scale-[1.005] ${className}`}> <div className={`rounded-lg border border-offbase bg-background px-3 py-2 transition-colors duration-fast ease-standard ${className}`}>
{children} {children}
</div> </div>
); );
@ -159,13 +159,13 @@ export function Switch({
aria-describedby={ariaDescribedBy} aria-describedby={ariaDescribedBy}
disabled={disabled} disabled={disabled}
onClick={() => onChange(!checked)} onClick={() => onChange(!checked)}
className={`relative inline-flex shrink-0 cursor-pointer items-center rounded-full border border-offbase transition-colors duration-200 ease-out focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed ${s.track} ${ className={`relative inline-flex shrink-0 cursor-pointer items-center rounded-full border border-offbase transition-colors duration-fast ease-standard focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed ${s.track} ${
checked ? 'bg-accent' : 'bg-offbase' checked ? 'bg-accent' : 'bg-offbase'
}`} }`}
> >
<span <span
aria-hidden="true" aria-hidden="true"
className={`pointer-events-none inline-block rounded-full bg-background shadow-sm ring-0 transition-transform duration-200 ease-out ${s.thumb} ${ className={`pointer-events-none inline-block rounded-full bg-background shadow-sm ring-0 transition-transform duration-fast ease-standard ${s.thumb} ${
checked ? s.on : s.off checked ? s.on : s.off
}`} }`}
/> />
@ -194,8 +194,8 @@ export function ToggleRow({
const descId = useId(); const descId = useId();
const rowClass = const rowClass =
variant === 'flat' variant === 'flat'
? 'px-0.5 pt-1 pb-2 border-b border-offbase last:border-b-0 transition-transform duration-200 ease-out hover:scale-[1.003]' ? 'px-0.5 pt-1 pb-2 border-b border-offbase last:border-b-0 transition-colors duration-fast ease-standard'
: 'rounded-md border border-offbase bg-background px-2.5 py-1.5 transition-transform duration-200 ease-out hover:scale-[1.005]'; : 'rounded-md border border-offbase bg-background px-2.5 py-1.5 transition-colors duration-fast ease-standard';
const handleTextToggle = () => { const handleTextToggle = () => {
if (!disabled) onChange(!checked); if (!disabled) onChange(!checked);
}; };
@ -243,7 +243,7 @@ export function CheckItem({
<span <span
id={labelId} id={labelId}
onClick={handleTextToggle} onClick={handleTextToggle}
className={`flex-1 min-w-0 truncate text-xs leading-4 text-foreground select-none transition-colors duration-200 ease-out group-hover:text-accent ${ className={`flex-1 min-w-0 truncate text-xs leading-4 text-foreground select-none transition-colors duration-fast ease-standard group-hover:text-accent ${
disabled ? '' : 'cursor-pointer' disabled ? '' : 'cursor-pointer'
}`} }`}
> >

View file

@ -56,7 +56,7 @@ export const Navigator = ({ currentPage, numPages, skipToLocation }: {
<Button <Button
onClick={() => skipToLocation(currentPage - 1, true)} onClick={() => skipToLocation(currentPage - 1, true)}
disabled={currentPage <= 1} disabled={currentPage <= 1}
className="relative p-2 rounded-full text-foreground hover:bg-offbase data-[hover]:bg-offbase data-[active]:bg-offbase transition-all duration-200 focus:outline-none disabled:opacity-50 transform ease-in-out hover:scale-[1.09] hover:text-accent" className="relative p-2 rounded-full text-foreground hover:bg-offbase data-[hover]:bg-offbase data-[active]:bg-offbase transition duration-base focus:outline-none disabled:opacity-50 transform ease-standard hover:text-accent"
aria-label="Previous page" aria-label="Previous page"
> >
<svg className="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor"> <svg className="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor">
@ -67,7 +67,7 @@ export const Navigator = ({ currentPage, numPages, skipToLocation }: {
{/* Page number popup */} {/* Page number popup */}
<Popover className="relative mb-1"> <Popover className="relative mb-1">
<PopoverButton <PopoverButton
className="bg-offbase px-2 py-0.5 rounded-full focus:outline-none cursor-pointer hover:bg-offbase transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-accent" className="bg-offbase px-2 py-0.5 rounded-full focus:outline-none cursor-pointer hover:bg-offbase transform transition-transform duration-base ease-standard hover:text-accent"
onClick={handlePopoverOpen} onClick={handlePopoverOpen}
> >
<p className="text-xs whitespace-nowrap"> <p className="text-xs whitespace-nowrap">
@ -99,7 +99,7 @@ export const Navigator = ({ currentPage, numPages, skipToLocation }: {
<Button <Button
onClick={() => skipToLocation(currentPage + 1, true)} onClick={() => skipToLocation(currentPage + 1, true)}
disabled={currentPage >= (numPages || 1)} disabled={currentPage >= (numPages || 1)}
className="relative p-2 rounded-full text-foreground hover:bg-offbase data-[hover]:bg-offbase data-[active]:bg-offbase transition-all duration-200 focus:outline-none disabled:opacity-50 transform ease-in-out hover:scale-[1.09] hover:text-accent" className="relative p-2 rounded-full text-foreground hover:bg-offbase data-[hover]:bg-offbase data-[active]:bg-offbase transition duration-base focus:outline-none disabled:opacity-50 transform ease-standard hover:text-accent"
aria-label="Next page" aria-label="Next page"
> >
<svg className="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor"> <svg className="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor">

View file

@ -16,7 +16,7 @@ export function RateLimitPauseButton() {
onClick={() => { onClick={() => {
if (isPlaying) togglePlay(); if (isPlaying) togglePlay();
}} }}
className="relative p-1.5 rounded-md text-foreground hover:bg-offbase transition-all duration-200 focus:outline-none h-8 w-8 flex items-center justify-center transform ease-in-out hover:scale-[1.09] hover:text-accent" className="relative p-1.5 rounded-md text-foreground hover:bg-offbase transition duration-base focus:outline-none h-8 w-8 flex items-center justify-center transform ease-standard hover:text-accent"
aria-label="Pause" aria-label="Pause"
> >
<PauseIcon className="w-5 h-5" /> <PauseIcon className="w-5 h-5" />

View file

@ -88,7 +88,7 @@ export const SpeedControl = ({
return ( return (
<Popover className="relative"> <Popover className="relative">
<PopoverButton className="flex items-center space-x-0.5 sm:space-x-1 bg-transparent text-foreground text-xs sm:text-sm focus:outline-none cursor-pointer hover:bg-offbase rounded pl-1.5 sm:pl-2 pr-0.5 sm:pr-1 py-0.5 sm:py-1 transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-accent"> <PopoverButton className="flex items-center space-x-0.5 sm:space-x-1 bg-transparent text-foreground text-xs sm:text-sm focus:outline-none cursor-pointer hover:bg-offbase rounded pl-1.5 sm:pl-2 pr-0.5 sm:pr-1 py-0.5 sm:py-1 transform transition-transform duration-base ease-standard hover:text-accent">
<SpeedometerIcon className="h-3 w-3 sm:h-3.5 sm:w-3.5" /> <SpeedometerIcon className="h-3 w-3 sm:h-3.5 sm:w-3.5" />
<span className="sm:hidden">{compactTriggerLabel}</span> <span className="sm:hidden">{compactTriggerLabel}</span>
<span className="hidden sm:inline">{triggerLabel}</span> <span className="hidden sm:inline">{triggerLabel}</span>

View file

@ -51,7 +51,7 @@ export default function TTSPlayer({ currentPage, numPages }: {
{/* Playback Controls */} {/* Playback Controls */}
<Button <Button
onClick={skipBackward} onClick={skipBackward}
className="relative p-1.5 rounded-md text-foreground hover:bg-offbase transition-all duration-200 focus:outline-none disabled:opacity-50 h-8 w-8 flex items-center justify-center transform ease-in-out hover:scale-[1.09] hover:text-accent" className="relative p-1.5 rounded-md text-foreground hover:bg-offbase transition duration-base focus:outline-none disabled:opacity-50 h-8 w-8 flex items-center justify-center transform ease-standard hover:text-accent"
aria-label="Skip backward" aria-label="Skip backward"
disabled={isProcessing} disabled={isProcessing}
> >
@ -60,7 +60,7 @@ export default function TTSPlayer({ currentPage, numPages }: {
<Button <Button
onClick={togglePlay} onClick={togglePlay}
className="relative p-1.5 rounded-md text-foreground hover:bg-offbase transition-all duration-200 focus:outline-none h-8 w-8 flex items-center justify-center transform ease-in-out hover:scale-[1.09] hover:text-accent" className="relative p-1.5 rounded-md text-foreground hover:bg-offbase transition duration-base focus:outline-none h-8 w-8 flex items-center justify-center transform ease-standard hover:text-accent"
aria-label={isPlaying ? 'Pause' : 'Play'} aria-label={isPlaying ? 'Pause' : 'Play'}
disabled={isProcessing && !isPlaying} disabled={isProcessing && !isPlaying}
> >
@ -69,7 +69,7 @@ export default function TTSPlayer({ currentPage, numPages }: {
<Button <Button
onClick={skipForward} onClick={skipForward}
className="relative p-1.5 rounded-md text-foreground hover:bg-offbase transition-all duration-200 focus:outline-none disabled:opacity-50 h-8 w-8 flex items-center justify-center transform ease-in-out hover:scale-[1.09] hover:text-accent" className="relative p-1.5 rounded-md text-foreground hover:bg-offbase transition duration-base focus:outline-none disabled:opacity-50 h-8 w-8 flex items-center justify-center transform ease-standard hover:text-accent"
aria-label="Skip forward" aria-label="Skip forward"
disabled={isProcessing} disabled={isProcessing}
> >

View file

@ -34,8 +34,8 @@ export function VoicesControlBase({
: 'bottom-full right-0 mb-1'; : 'bottom-full right-0 mb-1';
const buttonClass = variant === 'field' const buttonClass = variant === 'field'
? 'relative cursor-pointer rounded-lg bg-base py-1.5 pl-3 pr-10 text-left text-foreground focus:outline-none focus:ring-2 focus:ring-accent transform transition-transform duration-200 ease-in-out hover:scale-[1.01] hover:text-accent w-full' ? 'relative cursor-pointer rounded-lg bg-base py-1.5 pl-3 pr-10 text-left text-foreground focus:outline-none focus:ring-2 focus:ring-accent transform transition-transform duration-base ease-standard hover:text-accent w-full'
: 'flex items-center space-x-0.5 sm:space-x-1 bg-transparent text-foreground text-xs sm:text-sm focus:outline-none cursor-pointer hover:bg-offbase rounded pl-1.5 sm:pl-2 pr-0.5 sm:pr-1 py-0.5 sm:py-1 transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-accent'; : 'flex items-center space-x-0.5 sm:space-x-1 bg-transparent text-foreground text-xs sm:text-sm focus:outline-none cursor-pointer hover:bg-offbase rounded pl-1.5 sm:pl-2 pr-0.5 sm:pr-1 py-0.5 sm:py-1 transform transition-transform duration-base ease-standard hover:text-accent';
const iconClass = variant === 'field' const iconClass = variant === 'field'
? 'h-3.5 w-3.5 shrink-0' ? 'h-3.5 w-3.5 shrink-0'

View file

@ -41,10 +41,10 @@ export function ReaderSidebarShell({
> >
<Transition.Child <Transition.Child
as={Fragment} as={Fragment}
enter="transition-opacity ease-out duration-200" enter="transition-opacity ease-standard duration-base"
enterFrom="opacity-0" enterFrom="opacity-0"
enterTo="opacity-100" enterTo="opacity-100"
leave="transition-opacity ease-in duration-150" leave="transition-opacity ease-standard duration-fast"
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
@ -58,10 +58,10 @@ export function ReaderSidebarShell({
<Transition.Child <Transition.Child
as={Fragment} as={Fragment}
enter="transition ease-out duration-220" enter="transition ease-standard duration-base"
enterFrom="translate-x-full" enterFrom="translate-x-full"
enterTo="translate-x-0" enterTo="translate-x-0"
leave="transition ease-in duration-180" leave="transition ease-standard duration-base"
leaveFrom="translate-x-0" leaveFrom="translate-x-0"
leaveTo="translate-x-full" leaveTo="translate-x-full"
> >

View file

@ -901,10 +901,10 @@ function SegmentMetadataPopover({ row }: { row: TTSSegmentRow }) {
</PopoverButton> </PopoverButton>
<Transition <Transition
as={Fragment} as={Fragment}
enter="transition ease-out duration-150" enter="transition ease-standard duration-fast"
enterFrom="opacity-0 translate-y-1" enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0" enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-100" leave="transition ease-standard duration-fast"
leaveFrom="opacity-100 translate-y-0" leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1" leaveTo="opacity-0 translate-y-1"
> >

View file

@ -1,9 +1,9 @@
export const btnBase = export const btnBase =
'inline-flex items-center justify-center rounded-md text-sm font-medium focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 transition-colors transition-transform duration-200 ease-out disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100'; 'inline-flex items-center justify-center rounded-md text-sm font-medium focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 transition-colors duration-fast ease-standard disabled:opacity-50 disabled:cursor-not-allowed';
export const btnPrimary = `${btnBase} bg-accent text-background hover:bg-secondary-accent hover:scale-[1.03]`; export const btnPrimary = `${btnBase} bg-accent text-background hover:bg-secondary-accent`;
export const btnSecondary = `${btnBase} bg-base text-foreground border border-offbase hover:bg-offbase hover:scale-[1.03]`; export const btnSecondary = `${btnBase} bg-base text-foreground border border-offbase hover:bg-offbase`;
export const btnOutline = `${btnBase} bg-background border border-offbase text-foreground hover:bg-base hover:text-accent hover:scale-[1.02]`; export const btnOutline = `${btnBase} bg-background border border-offbase text-foreground hover:bg-base hover:text-accent`;
export const btnDanger = `${btnBase} bg-red-600 text-white border border-red-700 hover:bg-red-700 hover:scale-[1.02]`; export const btnDanger = `${btnBase} bg-danger text-background border border-danger hover:bg-danger`;
export const btnGhost = `${btnBase} bg-transparent text-foreground hover:bg-base hover:text-accent`; export const btnGhost = `${btnBase} bg-transparent text-foreground hover:bg-base hover:text-accent`;
export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'danger' | 'ghost'; export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'danger' | 'ghost';

View file

@ -163,7 +163,7 @@ export function EPUBViewer({ className = '', epubState }: EPUBViewerProps) {
<button <button
type="button" type="button"
onClick={() => setIsTocOpen(open => !open)} onClick={() => setIsTocOpen(open => !open)}
className="inline-flex items-center py-1 px-1 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition-all duration-200 ease-in-out transform hover:scale-[1.09] hover:text-accent" className="inline-flex items-center py-1 px-1 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition duration-base ease-standard transform hover:text-accent"
aria-label={isTocOpen ? 'Hide chapters' : 'Show chapters'} aria-label={isTocOpen ? 'Hide chapters' : 'Show chapters'}
> >
<DotsVerticalIcon className="w-4 h-4" /> <DotsVerticalIcon className="w-4 h-4" />
@ -171,7 +171,7 @@ export function EPUBViewer({ className = '', epubState }: EPUBViewerProps) {
<button <button
type="button" type="button"
onClick={() => handleLocationChanged('prev')} onClick={() => handleLocationChanged('prev')}
className="inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition-all duration-200 ease-in-out transform hover:scale-[1.09] hover:text-accent" className="inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition duration-base ease-standard transform hover:text-accent"
aria-label="Previous section" aria-label="Previous section"
> >
<ChevronLeftIcon className="w-4 h-4" /> <ChevronLeftIcon className="w-4 h-4" />
@ -185,7 +185,7 @@ export function EPUBViewer({ className = '', epubState }: EPUBViewerProps) {
<button <button
type="button" type="button"
onClick={() => handleLocationChanged('next')} onClick={() => handleLocationChanged('next')}
className="inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition-all duration-200 ease-in-out transform hover:scale-[1.09] hover:text-accent" className="inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition duration-base ease-standard transform hover:text-accent"
aria-label="Next section" aria-label="Next section"
> >
<ChevronRightIcon className="w-4 h-4" /> <ChevronRightIcon className="w-4 h-4" />
@ -205,7 +205,7 @@ export function EPUBViewer({ className = '', epubState }: EPUBViewerProps) {
}} }}
className=" className="
px-2 py-1 rounded-md font-medium text-foreground text-center bg-base px-2 py-1 rounded-md font-medium text-foreground text-center bg-base
hover:bg-offbase hover:text-accent transition-colors duration-150 hover:bg-offbase hover:text-accent transition-colors duration-fast
whitespace-nowrap whitespace-nowrap
flex-1 min-w-[140px] flex-1 min-w-[140px]
" "