build(deps): migrate Tailwind CSS v3.4 → v4
Root-fixes the opacity-modifier seed bug behind R10 S0: the theme colors were bare `var(--x)` hex with no <alpha-value>, so `bg-accent/40`, `ring-accent/40`, `border-border/60`, `text-muted/70`, … (~99 sites across 45 files) emitted no CSS rule at all — latent no-ops. v4 implements the opacity modifier via `color-mix`, so the modifier now works with the existing hex vars, no per-scheme token duplication needed. - `npx @tailwindcss/upgrade`: `@tailwind` directives → `@import "tailwindcss"`, JS config → CSS `@theme` (colors, --font-sans, the z-index tokens preserved), classname renames (rounded/shadow/blur scale, outline-none→outline-hidden, ring→ring-2) across 56 files; PostCSS plugin → @tailwindcss/postcss, autoprefixer removed (v4 prefixes for you). - `@theme` → `@theme inline` (load-bearing): the color vars are redefined at runtime under html[data-scheme]/[data-theme]; inline resolves them at the use site so per-scheme opacity tints track the active scheme (the shadcn v4 pattern). - Revert one codemod false-positive: it rewrote the English word "rounded" → "rounded-sm" inside a releaseNotes.ts prose sentence. Gate green: tsc(app+node) / eslint 0-err / prettier / vitest 79 / knip. Verified in-browser: bg-accent/15 now computes a scheme-tracking color at 0.15 alpha across all 6 schemes × 2 themes; the hand-rolled glass border/shadow/ radius/backdrop-filter render unchanged under v4.
This commit is contained in:
@@ -1110,7 +1110,7 @@ export default function PlexPlayer({ itemId, onClose, queue }: Props) {
|
||||
|
||||
{/* Top bar */}
|
||||
<div
|
||||
className={`absolute top-0 inset-x-0 p-4 bg-gradient-to-b from-black/70 to-transparent transition-opacity ${
|
||||
className={`absolute top-0 inset-x-0 p-4 bg-linear-to-b from-black/70 to-transparent transition-opacity ${
|
||||
uiVisible ? "opacity-100" : "opacity-0"
|
||||
}`}
|
||||
>
|
||||
@@ -1184,7 +1184,7 @@ export default function PlexPlayer({ itemId, onClose, queue }: Props) {
|
||||
|
||||
{/* Bottom controls */}
|
||||
<div
|
||||
className={`absolute bottom-0 inset-x-0 px-4 pb-3 pt-8 bg-gradient-to-t from-black/80 to-transparent transition-opacity ${
|
||||
className={`absolute bottom-0 inset-x-0 px-4 pb-3 pt-8 bg-linear-to-t from-black/80 to-transparent transition-opacity ${
|
||||
uiVisible ? "opacity-100" : "opacity-0 pointer-events-none"
|
||||
}`}
|
||||
>
|
||||
@@ -1221,7 +1221,7 @@ export default function PlexPlayer({ itemId, onClose, queue }: Props) {
|
||||
{/* Hover timestamp tooltip — follows the cursor along the bar. */}
|
||||
{hover && duration > 0 && (
|
||||
<div
|
||||
className="pointer-events-none absolute bottom-full mb-2 -translate-x-1/2 rounded bg-black/90 px-1.5 py-0.5 text-[11px] tabular-nums text-white"
|
||||
className="pointer-events-none absolute bottom-full mb-2 -translate-x-1/2 rounded-sm bg-black/90 px-1.5 py-0.5 text-[11px] tabular-nums text-white"
|
||||
style={{ left: `${hover.frac * 100}%` }}
|
||||
>
|
||||
{fmt(hover.t)}
|
||||
@@ -1311,13 +1311,13 @@ export default function PlexPlayer({ itemId, onClose, queue }: Props) {
|
||||
/>
|
||||
{/* thumb */}
|
||||
<div
|
||||
className="absolute top-1/2 h-3 w-3 -translate-x-1/2 -translate-y-1/2 rounded-full bg-accent shadow"
|
||||
className="absolute top-1/2 h-3 w-3 -translate-x-1/2 -translate-y-1/2 rounded-full bg-accent shadow-sm"
|
||||
style={{ left: `${(muted ? 0 : volume) * 100}%` }}
|
||||
/>
|
||||
{/* hover value tooltip (0–100), like the seek bar */}
|
||||
{volHover != null && (
|
||||
<div
|
||||
className="pointer-events-none absolute bottom-full mb-2 -translate-x-1/2 rounded bg-black/90 px-1.5 py-0.5 text-[11px] tabular-nums text-white"
|
||||
className="pointer-events-none absolute bottom-full mb-2 -translate-x-1/2 rounded-sm bg-black/90 px-1.5 py-0.5 text-[11px] tabular-nums text-white"
|
||||
style={{ left: `${volHover * 100}%` }}
|
||||
>
|
||||
{Math.round(volHover * 100)}
|
||||
@@ -1544,7 +1544,7 @@ export default function PlexPlayer({ itemId, onClose, queue }: Props) {
|
||||
type="color"
|
||||
value={prefs.subColor}
|
||||
onChange={(e) => patchPrefs({ subColor: e.target.value })}
|
||||
className="h-6 w-10 cursor-pointer rounded bg-transparent"
|
||||
className="h-6 w-10 cursor-pointer rounded-sm bg-transparent"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between px-1 py-1.5">
|
||||
@@ -1570,7 +1570,7 @@ export default function PlexPlayer({ itemId, onClose, queue }: Props) {
|
||||
type="color"
|
||||
value={prefs.subBg}
|
||||
onChange={(e) => patchPrefs({ subBg: e.target.value })}
|
||||
className="h-6 w-10 cursor-pointer rounded bg-transparent"
|
||||
className="h-6 w-10 cursor-pointer rounded-sm bg-transparent"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -1593,7 +1593,7 @@ export default function PlexPlayer({ itemId, onClose, queue }: Props) {
|
||||
type="color"
|
||||
value={prefs.subShadowColor}
|
||||
onChange={(e) => patchPrefs({ subShadowColor: e.target.value })}
|
||||
className="h-6 w-10 cursor-pointer rounded bg-transparent"
|
||||
className="h-6 w-10 cursor-pointer rounded-sm bg-transparent"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -1629,7 +1629,7 @@ export default function PlexPlayer({ itemId, onClose, queue }: Props) {
|
||||
type="color"
|
||||
value={prefs.clockColor}
|
||||
onChange={(e) => patchPrefs({ clockColor: e.target.value })}
|
||||
className="h-6 w-10 cursor-pointer rounded bg-transparent"
|
||||
className="h-6 w-10 cursor-pointer rounded-sm bg-transparent"
|
||||
/>
|
||||
</div>
|
||||
<Toggle
|
||||
@@ -1728,7 +1728,7 @@ export default function PlexPlayer({ itemId, onClose, queue }: Props) {
|
||||
{keys.map((k) => (
|
||||
<kbd
|
||||
key={k}
|
||||
className="rounded bg-white/15 px-1.5 py-0.5 text-xs font-mono whitespace-nowrap"
|
||||
className="rounded-sm bg-white/15 px-1.5 py-0.5 text-xs font-mono whitespace-nowrap"
|
||||
>
|
||||
{k}
|
||||
</kbd>
|
||||
@@ -1879,7 +1879,7 @@ function Ctrl({
|
||||
{children}
|
||||
</button>
|
||||
<span
|
||||
className={`pointer-events-none absolute bottom-full ${pos} mb-2 whitespace-nowrap rounded bg-black/90 px-2 py-1 text-[11px] text-white opacity-0 transition group-hover/ctrl:opacity-100`}
|
||||
className={`pointer-events-none absolute bottom-full ${pos} mb-2 whitespace-nowrap rounded-sm bg-black/90 px-2 py-1 text-[11px] text-white opacity-0 transition group-hover/ctrl:opacity-100`}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user