From 658f22bb7f5d1cb3a5d8115c4cd316ff2a5a711f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Thu, 1 Oct 2026 07:21:05 -0700 Subject: [PATCH] feat(studio): graphite colour system with a light theme that passes contrast (#4836) * feat(studio): graphite colour system with a light theme that passes contrast Studio's tokens take the Graphite values: a lifted dark, a light set behind data-theme="paper", the player dark in both. Every text role clears 4.5:1 and every icon and ring 3:1 on the fills it sits on, held by the contrast ratchet. * fix(studio): every studio surface follows the light theme and passes contrast The dock no longer inherits dockview's forced dark scheme, popovers sit on the raised surface, stock white, red and amber text map to roles, the keyframe diamonds and track headers use roles, and the contrast ratchet measures every pair in both themes (150 pairs). * fix(studio): stock white, red and amber stay colours; studio text and fills use roles Round-two review: white, red and amber are colour content as well as text, so they keep Tailwind's values and hosts keep their palettes. Text, toasts, the feedback card, the FX popover, destructive buttons, category labels and the dock take Graphite roles instead; the contrast gate rejects an unparseable colour. * fix(studio): every light-theme ink, icon, dot and focus ring holds its contrast Stock white, red, amber and file-type hex left in Studio chrome now use roles or a light-dark pair; the file tree icons carry a contrast test. * chore(studio): drop two comments the names already carry * fix(studio): red marks, teal outlines and colour curves hold contrast in both themes Danger graphics use the danger ink, file tree outlines use the accent ink, the colour-curve well stays dark like the scopes, and six timeline token pins come back. * fix(studio): invalid readouts, the curve focus ring and editor dots stay distinct in both themes The invalid readout border is dashed as well as red, the curve graph's ring sits inside its dark well, the radius dots and ease handle centre follow the theme. * refactor(studio): ease and radius editors use colour classes; the dashed invalid border has a test Simplify pass: the ease editor's inline accent styles become stroke and fill classes, the radius dot class is computed once, the ratio check is one expression, and the alias table is named for what it holds. --- .../components/CaptionAnimationPanel.tsx | 4 +- .../components/CaptionPropertyPanel.tsx | 4 +- .../captions/components/CaptionTimeline.tsx | 6 +- .../studio/src/components/AskAgentModal.tsx | 2 +- packages/studio/src/components/LintModal.tsx | 70 ++-- .../src/components/StudioErrorBoundary.tsx | 2 +- .../src/components/StudioHeader.dom.test.tsx | 8 +- .../studio/src/components/StudioHeader.tsx | 3 +- .../studio/src/components/StudioToast.tsx | 23 +- .../src/components/TimelineToolPicker.tsx | 2 +- .../studio/src/components/TimelineToolbar.tsx | 22 +- packages/studio/src/components/dock/Dock.tsx | 2 +- packages/studio/src/components/dock/dock.css | 10 +- .../src/components/editor/AnimationCard.tsx | 10 +- .../components/editor/AnimationCardParts.tsx | 2 +- .../components/editor/BlockParamsPanel.tsx | 6 +- .../components/editor/BorderRadiusEditor.tsx | 23 +- .../components/editor/CanvasContextMenu.tsx | 2 +- .../components/editor/ComputedTweenNotice.tsx | 2 +- .../components/editor/DomEditRotateHandle.tsx | 2 +- .../components/editor/EaseCurveSection.tsx | 33 +- .../components/editor/EaseModeControls.tsx | 4 +- .../src/components/editor/EaseParamFields.tsx | 16 +- .../studio/src/components/editor/FileTree.tsx | 2 +- .../src/components/editor/FileTreeIcons.tsx | 73 ++-- .../src/components/editor/FileTreeNodes.tsx | 19 +- .../editor/GestureRecordControl.tsx | 6 +- .../editor/GsapAddAnimationControl.tsx | 2 +- .../editor/GsapAnimationSection.tsx | 4 +- .../components/editor/InlineTextToolbar.tsx | 8 +- .../editor/InspectorHeaderActions.tsx | 2 +- .../src/components/editor/KeyframeDiamond.tsx | 10 +- .../components/editor/KeyframeNavigation.tsx | 8 +- .../src/components/editor/LayersPanel.tsx | 6 +- .../components/editor/PromotableControl.tsx | 4 +- .../editor/PropertyPanelEmptyState.tsx | 8 +- .../editor/PropertyPanelFlatFooter.tsx | 4 +- .../editor/PropertyPanelFlatHeader.test.tsx | 2 +- .../editor/PropertyPanelFlatHeader.tsx | 4 +- .../src/components/editor/SnapToolbar.tsx | 10 +- .../components/editor/TimelineFxPopover.tsx | 8 +- .../src/components/editor/Transform3DCube.tsx | 2 +- .../src/components/editor/easeCurveSvg.tsx | 2 +- .../editor/propertyPanelColorCurveGraph.tsx | 4 +- .../propertyPanelColorGradingControls.tsx | 2 +- .../propertyPanelColorGradingSection.tsx | 16 +- .../propertyPanelColorGradingSlider.tsx | 2 +- .../editor/propertyPanelColorSecondary.tsx | 8 +- .../components/editor/propertyPanelFill.tsx | 12 +- ...propertyPanelFlatColorGradingAccessory.tsx | 6 +- ...pertyPanelFlatColorGradingSection.test.tsx | 2 +- .../propertyPanelFlatColorGradingSection.tsx | 15 +- .../propertyPanelFlatEffectsSection.tsx | 8 +- .../editor/propertyPanelFlatMediaSection.tsx | 4 +- .../editor/propertyPanelFlatMotionSection.tsx | 4 +- .../propertyPanelFlatOverlaysSection.tsx | 4 +- .../propertyPanelFlatPrimitives.test.tsx | 7 +- .../editor/propertyPanelFlatPrimitives.tsx | 2 +- .../editor/propertyPanelFlatSliderReadout.tsx | 2 +- .../components/editor/propertyPanelFont.tsx | 4 +- .../editor/propertyPanelFxCarveModule.tsx | 2 +- .../editor/propertyPanelFxControls.tsx | 4 +- .../editor/propertyPanelFxEqModule.tsx | 4 +- .../editor/propertyPanelFxNodeRow.tsx | 2 +- .../editor/propertyPanelFxPresetRun.tsx | 2 +- .../editor/propertyPanelMediaSection.tsx | 2 +- .../editor/propertyPanelPrimitives.tsx | 2 +- .../editor/propertyPanelSections.tsx | 4 +- .../editor/propertyPanelValueTier.test.ts | 2 +- .../editor/propertyPanelValueTier.ts | 2 +- .../feedback/StudioFeedbackCard.tsx | 34 +- .../components/nle/AssetPreviewOverlay.tsx | 2 +- .../src/components/nle/AudioMeterStrip.tsx | 4 +- .../src/components/nle/PreviewOverlays.tsx | 2 +- .../studio/src/components/nle/PreviewPane.tsx | 6 +- .../src/components/nle/PreviewZoomOverlay.tsx | 2 +- .../src/components/panels/SlideshowPanel.tsx | 8 +- .../components/panels/SlideshowSubPanels.tsx | 40 +- .../panels/VariablesBindElement.tsx | 2 +- .../panels/VariablesDeclarationForm.tsx | 2 +- .../src/components/panels/VariablesPanel.tsx | 10 +- .../components/panels/VariablesRowAction.tsx | 2 +- .../renders/FfmpegRequiredNotice.tsx | 8 +- .../src/components/renders/RenderQueue.tsx | 4 +- .../components/renders/RenderQueueItem.tsx | 6 +- .../src/components/sidebar/AssetCard.tsx | 6 +- .../components/sidebar/AssetContextMenu.tsx | 14 +- .../src/components/sidebar/AssetsTab.tsx | 12 +- .../src/components/sidebar/AudioRow.tsx | 8 +- .../src/components/sidebar/BlocksTab.tsx | 12 +- .../components/sidebar/CompositionsTab.tsx | 6 +- .../components/sidebar/PromptPreviewModal.tsx | 6 +- .../components/sidebar/SidebarLintButton.tsx | 6 +- .../src/components/timelineToolbarStyles.ts | 6 +- packages/studio/src/components/ui/Button.tsx | 8 +- .../src/components/ui/HyperframesLogo.tsx | 37 +- packages/studio/src/components/ui/Input.tsx | 2 +- packages/studio/src/components/ui/Menu.tsx | 6 +- .../src/player/components/ClipContextMenu.tsx | 2 +- .../components/CompositionThumbnail.tsx | 2 +- .../src/player/components/EditModal.tsx | 12 +- .../components/KeyframeDiamondContextMenu.tsx | 4 +- .../src/player/components/PlayerControls.tsx | 6 +- .../src/player/components/ShortcutsPanel.tsx | 28 +- .../src/player/components/TimelineCanvas.tsx | 2 +- .../src/player/components/TimelineClip.tsx | 4 +- .../player/components/TimelineEmptyState.tsx | 4 +- .../src/player/components/TimelineLanes.tsx | 1 - .../src/player/components/TrackClipCount.tsx | 2 +- .../src/player/components/VolumeControl.tsx | 2 +- .../components/timelineClipChildren.tsx | 2 +- .../components/timelineMotionStyles.test.ts | 28 +- .../components/trackHeaderLabelRows.tsx | 32 +- packages/studio/src/styles/hex-baseline.json | 26 +- packages/studio/src/styles/studio.css | 9 - .../src/styles/tailwind-preset.shared.js | 3 +- packages/studio/src/styles/theme.css | 345 ++++++++++-------- packages/studio/src/styles/theme.test.ts | 22 +- packages/studio/src/utils/blockCategories.ts | 50 ++- scripts/contrast-baseline.json | 14 +- scripts/contrast-pairs.json | 210 ++++++++--- scripts/contrast.ts | 185 ++++++++-- scripts/contrastRatchet.test.ts | 75 +++- 123 files changed, 1111 insertions(+), 790 deletions(-) diff --git a/packages/studio/src/captions/components/CaptionAnimationPanel.tsx b/packages/studio/src/captions/components/CaptionAnimationPanel.tsx index 6983b9ebef..e426404c38 100644 --- a/packages/studio/src/captions/components/CaptionAnimationPanel.tsx +++ b/packages/studio/src/captions/components/CaptionAnimationPanel.tsx @@ -250,7 +250,7 @@ export const CaptionAnimationPanel = memo(function CaptionAnimationPanel() {
{gated && (
-

+

Animation editing isn't applied to playback or saved yet, so these controls are disabled.

@@ -292,7 +292,7 @@ export const CaptionAnimationPanel = memo(function CaptionAnimationPanel() { onClick={handleApplyToAll} disabled={gated} title={gated ? "Disabled until animation editing is applied to playback" : undefined} - className="w-full py-1.5 rounded-sm border border-neutral-700 text-2xs text-neutral-300 hover:border-studio-accent/50 hover:text-studio-accent transition-colors disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:border-neutral-700 disabled:hover:text-neutral-300" + className="w-full py-1.5 rounded-sm border border-neutral-700 text-2xs text-neutral-300 hover:border-studio-accent/50 hover:text-accent-ink transition-colors disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:border-neutral-700 disabled:hover:text-neutral-300" > Apply to all groups diff --git a/packages/studio/src/captions/components/CaptionPropertyPanel.tsx b/packages/studio/src/captions/components/CaptionPropertyPanel.tsx index bab6f0001e..f1ea71f41c 100644 --- a/packages/studio/src/captions/components/CaptionPropertyPanel.tsx +++ b/packages/studio/src/captions/components/CaptionPropertyPanel.tsx @@ -227,7 +227,7 @@ export const CaptionPropertyPanel = memo(function CaptionPropertyPanel({ className={[ "flex-1 py-0.5 rounded-sm text-2xs font-medium transition-colors", activeTab === "style" - ? "bg-studio-accent/20 text-studio-accent border border-studio-accent/50" + ? "bg-studio-accent/20 text-accent-ink border border-studio-accent/50" : "text-neutral-500 border border-neutral-800 hover:text-neutral-300 hover:border-neutral-600", ].join(" ")} > @@ -241,7 +241,7 @@ export const CaptionPropertyPanel = memo(function CaptionPropertyPanel({ className={[ "flex-1 py-0.5 rounded-sm text-2xs font-medium transition-colors", activeTab === "animation" - ? "bg-studio-accent/20 text-studio-accent border border-studio-accent/50" + ? "bg-studio-accent/20 text-accent-ink border border-studio-accent/50" : "text-neutral-500 border border-neutral-800 hover:text-neutral-300 hover:border-neutral-600", ].join(" ")} > diff --git a/packages/studio/src/captions/components/CaptionTimeline.tsx b/packages/studio/src/captions/components/CaptionTimeline.tsx index 679d731c82..22a2c6ba8f 100644 --- a/packages/studio/src/captions/components/CaptionTimeline.tsx +++ b/packages/studio/src/captions/components/CaptionTimeline.tsx @@ -109,8 +109,8 @@ export const CaptionTimeline = memo(function CaptionTimeline({ tabIndex={0} aria-label={`Caption word "${seg.text}"`} aria-pressed={isSelected} - className={`absolute top-1 bottom-1 rounded flex items-center overflow-hidden cursor-pointer focus-visible:ring-1 focus-visible:ring-white outline-hidden${ - isSelected ? " ring-1 ring-white/50 z-10" : "" + className={`absolute top-1 bottom-1 rounded flex items-center overflow-hidden cursor-pointer focus-visible:ring-1 focus-visible:ring-text-0 outline-hidden${ + isSelected ? " ring-1 ring-text-0/50 z-10" : "" }`} style={{ left, @@ -135,7 +135,7 @@ export const CaptionTimeline = memo(function CaptionTimeline({ {/* Playhead — correlates blocks with the current frame */}
); } + +function LintFindingRow({ finding, tone }: { finding: LintFinding; tone: "error" | "warning" }) { + return ( +
+
+ +
+

+ {finding.message} +

+ {finding.file && ( +

{finding.file}

+ )} + {finding.fixHint && ( +
+ +

{finding.fixHint}

+
+ )} +
+
+
+ ); +} diff --git a/packages/studio/src/components/StudioErrorBoundary.tsx b/packages/studio/src/components/StudioErrorBoundary.tsx index 5a5924aa01..4dbf1ca2d9 100644 --- a/packages/studio/src/components/StudioErrorBoundary.tsx +++ b/packages/studio/src/components/StudioErrorBoundary.tsx @@ -39,7 +39,7 @@ export class StudioErrorBoundary extends Component {
diff --git a/packages/studio/src/components/StudioHeader.dom.test.tsx b/packages/studio/src/components/StudioHeader.dom.test.tsx index 45b926e910..fe4d01f8b7 100644 --- a/packages/studio/src/components/StudioHeader.dom.test.tsx +++ b/packages/studio/src/components/StudioHeader.dom.test.tsx @@ -139,16 +139,16 @@ it("shows Inspector pressed and filled only when on", () => { const host = mount({ inspectorButtonActive: true }); const on = query(host, '[aria-label="Inspector"]'); expect(on.getAttribute("aria-pressed")).toBe("true"); - expect(hasToken(on.className, "text-accent")).toBe(true); - expect(hasToken(on.className, "bg-hover")).toBe(true); + expect(hasToken(on.className, "text-accent-ink")).toBe(true); + expect(hasToken(on.className, "bg-on")).toBe(true); act(() => mounted?.root.unmount()); mounted?.host.remove(); mounted = null; const off = query(mount(), '[aria-label="Inspector"]'); expect(off.getAttribute("aria-pressed")).toBe("false"); - expect(hasToken(off.className, "text-accent")).toBe(false); - expect(hasToken(off.className, "bg-hover")).toBe(false); + expect(hasToken(off.className, "text-accent-ink")).toBe(false); + expect(hasToken(off.className, "bg-on")).toBe(false); }); it("keeps Capture a real download link rather than a button", () => { diff --git a/packages/studio/src/components/StudioHeader.tsx b/packages/studio/src/components/StudioHeader.tsx index 3b9380fca1..a08acc5a49 100644 --- a/packages/studio/src/components/StudioHeader.tsx +++ b/packages/studio/src/components/StudioHeader.tsx @@ -126,7 +126,8 @@ export function StudioHeader({ aria-pressed={inspectorButtonActive} className={cn( "h-full rounded-none", - inspectorButtonActive && "bg-hover text-accent enabled:hover:text-accent", + inspectorButtonActive && + "bg-on text-accent-ink enabled:hover:bg-on-hover enabled:hover:text-accent-ink", )} icon={} onClick={() => { diff --git a/packages/studio/src/components/StudioToast.tsx b/packages/studio/src/components/StudioToast.tsx index 36c06b843a..b6c678b767 100644 --- a/packages/studio/src/components/StudioToast.tsx +++ b/packages/studio/src/components/StudioToast.tsx @@ -14,23 +14,10 @@ export function StudioToast({ message, tone, leaving, onDismiss }: StudioToastPr className={`motion-reduce:animate-none ${leaving ? "hf-toast-exit" : "hf-toast-enter"}`} >
{message} @@ -38,7 +25,7 @@ export function StudioToast({ message, tone, leaving, onDismiss }: StudioToastPr ))} diff --git a/packages/studio/src/components/dock/dock.css b/packages/studio/src/components/dock/dock.css index 23a7f940b2..4c2bcb6add 100644 --- a/packages/studio/src/components/dock/dock.css +++ b/packages/studio/src/components/dock/dock.css @@ -1,9 +1,17 @@ @import "dockview-react/dist/styles/dockview.css"; +.dv-shell:has(.hf-dock) { + /* dockview's runtime stylesheet pins `color-scheme: dark` on its theme class, which sits on + * the shell around the dock. */ + color-scheme: inherit; +} + .hf-dock { --dv-group-view-background-color: var(--panel-card-bg); --dv-tabs-and-actions-container-background-color: var(--panel-strip-bg); --dv-tabs-and-actions-container-height: 36px; + --dv-icon-hover-background-color: var(--color-hover); + --dv-tabs-container-scrollbar-color: var(--color-text-muted); /* The fill paints the shown tab, so every tab state is transparent. */ --dv-activegroup-visiblepanel-tab-background-color: transparent; --dv-activegroup-hiddenpanel-tab-background-color: transparent; @@ -178,7 +186,7 @@ } .dv-groupview:hover .hf-dock-strip-actions { - opacity: 0.5; + opacity: 0.8; } .hf-dock-strip-actions:hover, diff --git a/packages/studio/src/components/editor/AnimationCard.tsx b/packages/studio/src/components/editor/AnimationCard.tsx index 90ec786b2f..19ea4fabc8 100644 --- a/packages/studio/src/components/editor/AnimationCard.tsx +++ b/packages/studio/src/components/editor/AnimationCard.tsx @@ -201,7 +201,7 @@ export const AnimationCard = memo(function AnimationCard({ className="flex w-full items-center gap-2 py-1.5 active:scale-[0.99]" > {methodLabel} @@ -354,7 +354,7 @@ export const AnimationCard = memo(function AnimationCard({ {animation.method === "fromTo" && (
-

+

From

@@ -378,14 +378,14 @@ export const AnimationCard = memo(function AnimationCard({ onAdd={(prop) => onAddFromProperty?.(animation.id, prop)} onOpen={() => setAddingFromProp(true)} onClose={() => setAddingFromProp(false)} - buttonClassName="text-[11px] font-medium text-orange-400/70 transition-colors hover:text-orange-300" + buttonClassName="text-[11px] font-medium text-warning-ink transition-colors hover:text-text-0" />
)} {animation.method === "fromTo" && Object.keys(animation.properties).length > 0 && ( -

+

To

)} @@ -455,7 +455,7 @@ export const AnimationCard = memo(function AnimationCard({
@@ -220,7 +220,7 @@ export function WiggleField({ commitWiggle(onCommit, config.wiggles, event.currentTarget.value, amplitude); } }} - className="rounded-sm border border-white/10 bg-black/20 px-1.5 py-1 text-[10px] text-neutral-300 outline-hidden focus:border-panel-accent/50" + className="rounded-sm border border-border bg-input px-1.5 py-1 text-[10px] text-neutral-300 outline-hidden focus:border-panel-accent/50" > {WIGGLE_TYPES.map((type) => (
diff --git a/packages/studio/src/components/editor/FileTree.tsx b/packages/studio/src/components/editor/FileTree.tsx index ad6eddddc3..cc6b392f13 100644 --- a/packages/studio/src/components/editor/FileTree.tsx +++ b/packages/studio/src/components/editor/FileTree.tsx @@ -263,7 +263,7 @@ export const FileTree = memo(function FileTree({
([ + ["html", [FileHtml, RED]], + ["css", [FileCss, "text-[light-dark(oklch(0.5_0.2_266),oklch(0.71_0.2_266))]"]], + ["js", [FileJs, "text-[light-dark(oklch(0.56_0.156_101),#F0DB4F)]"]], + ["jsx", [FileJsx, "text-[light-dark(oklch(0.55_0.117_219),#61DAFB)]"]], + ["ts", [FileTs, BLUE]], + ["tsx", [FileTsx, BLUE]], + ["json", [FileCode, "text-[light-dark(oklch(0.54_0.182_152),#4ADE80)]"]], + ["svg", [FileSvg, "text-[light-dark(oklch(0.58_0.187_48),#F97316)]"]], + ["md", [FileMd, GRAY]], + ["txt", [FileTxt, GRAY]], + ["png", [FilePng, GREEN]], + ["jpg", [FileJpg, GREEN]], + ["webp", [PhImage, GREEN]], + ["mp4", [FileVideo, "text-[light-dark(oklch(0.59_0.2_304),oklch(0.71_0.2_304))]"]], + ["mp3", [Waveform, "text-[light-dark(oklch(0.53_0.159_165),#3CE6AC)]"]], + ["woff", [TextAa, DIM]], +]); +const ALIAS = new Map( + Object.entries({ + mjs: "js", + cjs: "js", + mts: "ts", + mdx: "md", + jpeg: "jpg", + gif: "webp", + ico: "webp", + webm: "mp4", + mov: "mp4", + wav: "mp3", + ogg: "mp3", + m4a: "mp3", + woff2: "woff", + ttf: "woff", + otf: "woff", + }), +); + export function FileIcon({ path }: { path: string }) { const ext = path.split(".").pop()?.toLowerCase() ?? ""; - const c = "shrink-0"; - if (ext === "html") return ; - if (ext === "css") return ; - if (ext === "js" || ext === "mjs" || ext === "cjs") - return ; - if (ext === "jsx") return ; - if (ext === "ts" || ext === "mts") - return ; - if (ext === "tsx") return ; - if (ext === "json") return ; - if (ext === "svg") return ; - if (ext === "md" || ext === "mdx") - return ; - if (ext === "txt") return ; - if (ext === "png") return ; - if (ext === "jpg" || ext === "jpeg") - return ; - if (ext === "webp" || ext === "gif" || ext === "ico") - return ; - if (ext === "mp4" || ext === "webm" || ext === "mov") - return ; - if (ext === "mp3" || ext === "wav" || ext === "ogg" || ext === "m4a") - return ; - if (ext === "woff" || ext === "woff2" || ext === "ttf" || ext === "otf") - return ; - return ; + const [Glyph, color] = ICONS.get(ALIAS.get(ext) ?? ext) ?? [File, DIM]; + return ; } // ── Tree Types ── diff --git a/packages/studio/src/components/editor/FileTreeNodes.tsx b/packages/studio/src/components/editor/FileTreeNodes.tsx index 5b8b7af6f4..b31907ad0b 100644 --- a/packages/studio/src/components/editor/FileTreeNodes.tsx +++ b/packages/studio/src/components/editor/FileTreeNodes.tsx @@ -16,6 +16,7 @@ import { type TreeNode, type ContextMenuState, type InlineInputState, + DIM, } from "./FileTreeIcons"; export type { ContextMenuState, InlineInputState }; @@ -173,7 +174,7 @@ export function ContextMenu({
@@ -419,7 +420,7 @@ export const TreeFile = memo(function TreeFile({ {node.name} {lintInfo && lintInfo.count > 0 && ( {lintInfo.count} @@ -505,7 +506,9 @@ export const TreeFolder = memo(function TreeFolder({ }} onDragLeave={onDragLeave} className={`w-full flex items-center gap-1.5 px-2.5 py-1 min-h-7 text-left text-xs text-neutral-400 hover:bg-neutral-800/30 hover:text-neutral-300 transition-colors ${ - isDragOver ? "bg-[#3CE6AC]/10 outline-solid outline-1 outline-[#3CE6AC]/40" : "" + isDragOver + ? "bg-accent/10 outline-solid outline-1 outline-accent-ink -outline-offset-1" + : "" }`} style={{ paddingLeft: `${8 + depth * 12}px` }} > diff --git a/packages/studio/src/components/editor/GestureRecordControl.tsx b/packages/studio/src/components/editor/GestureRecordControl.tsx index 2fb3e20a37..54ff70d452 100644 --- a/packages/studio/src/components/editor/GestureRecordControl.tsx +++ b/packages/studio/src/components/editor/GestureRecordControl.tsx @@ -43,7 +43,7 @@ export function GestureRecordPanelButton({ }} className={`w-full flex items-center justify-center gap-2 rounded-lg py-2 text-[11px] font-medium transition-colors ${ recording - ? "bg-red-500/15 text-red-400 border border-red-500/30 animate-pulse" + ? "bg-red-500/15 text-danger-ink border border-red-500/30 animate-pulse" : "bg-panel-input text-panel-text-2 hover:bg-panel-hover border border-panel-border" }`} > @@ -77,8 +77,8 @@ export function GestureRecordBadge({ title={label} className={`pointer-events-auto absolute z-20 flex h-7 w-7 items-center justify-center rounded-full border shadow-lg transition-colors ${ recording - ? "border-red-400/60 bg-red-500 text-white animate-pulse" - : "border-studio-accent/60 bg-neutral-950 text-studio-accent hover:bg-neutral-900" + ? "border-danger/60 bg-danger text-on-danger animate-pulse" + : "border-studio-accent/60 bg-neutral-950 text-accent-ink hover:bg-neutral-900" }`} style={{ left: Math.max(0, rect.left + rect.width + 8), diff --git a/packages/studio/src/components/editor/GsapAddAnimationControl.tsx b/packages/studio/src/components/editor/GsapAddAnimationControl.tsx index 8447adba8f..062a7828cf 100644 --- a/packages/studio/src/components/editor/GsapAddAnimationControl.tsx +++ b/packages/studio/src/components/editor/GsapAddAnimationControl.tsx @@ -3,7 +3,7 @@ import { ADD_METHODS, ADD_METHOD_LABELS, METHOD_TOOLTIPS } from "./gsapAnimation const STYLES = { classic: { method: - "rounded-lg border border-neutral-700 bg-neutral-900 px-2.5 py-1.5 text-[11px] font-medium text-neutral-300 transition-colors hover:border-neutral-600 hover:text-white", + "rounded-lg border border-neutral-700 bg-neutral-900 px-2.5 py-1.5 text-[11px] font-medium text-neutral-300 transition-colors hover:border-neutral-600 hover:text-text-0", cancel: "px-1.5 text-[11px] text-neutral-500 hover:text-neutral-300", trigger: "text-[11px] font-medium text-neutral-400 transition-colors hover:text-neutral-200", }, diff --git a/packages/studio/src/components/editor/GsapAnimationSection.tsx b/packages/studio/src/components/editor/GsapAnimationSection.tsx index 4f9e50494e..146d05e3c0 100644 --- a/packages/studio/src/components/editor/GsapAnimationSection.tsx +++ b/packages/studio/src/components/editor/GsapAnimationSection.tsx @@ -24,13 +24,13 @@ export const GsapAnimationSection = memo(function GsapAnimationSection({ return (
}> {multipleTimelines && ( -

+

This file has multiple GSAP timelines. Animation editing is disabled to prevent data loss — consolidate into a single timeline to enable editing.

)} {unsupportedTimelinePattern && ( -

+

This timeline uses a computed key (window.__timelines[variable]) the editor can't resolve statically. Use a string-literal key (window.__timelines["id"]) or a variable declaration (const tl = gsap.timeline()) to enable editing. diff --git a/packages/studio/src/components/editor/InlineTextToolbar.tsx b/packages/studio/src/components/editor/InlineTextToolbar.tsx index 1ca6d104c6..edf3c0bbf0 100644 --- a/packages/studio/src/components/editor/InlineTextToolbar.tsx +++ b/packages/studio/src/components/editor/InlineTextToolbar.tsx @@ -77,7 +77,7 @@ export function InlineTextToolbar({ data-inline-text-toolbar="true" role="toolbar" aria-label="Text formatting" - className="pointer-events-auto fixed z-200 flex items-center gap-1 rounded-lg border border-white/10 bg-[#15171c] p-1 shadow-[0_8px_24px_rgba(0,0,0,0.55)]" + className="pointer-events-auto fixed z-200 flex items-center gap-1 rounded-lg border border-border bg-raised p-1 shadow-[0_8px_24px_rgba(0,0,0,0.55)]" style={{ left: placement.left, top: placement.top, @@ -98,12 +98,12 @@ export function InlineTextToolbar({ onClick={(event) => event.stopPropagation()} >

- ● + ● Record a gesture @@ -33,7 +33,7 @@ function FlatEmptyState() { - ✦ + ✦ Describe a change to the agent @@ -50,7 +50,7 @@ function elementKindGlyph(element: DomEditSelection): { glyph: string; className return { glyph: "◆", className: "bg-panel-media/10 text-panel-media" }; } if (element.textFields?.length > 0) { - return { glyph: "T", className: "bg-panel-accent/10 text-panel-accent" }; + return { glyph: "T", className: "bg-panel-accent/10 text-accent-ink" }; } return { glyph: "▦", className: "bg-panel-container/10 text-panel-container" }; } @@ -74,7 +74,7 @@ function FlatMultiSelectState({ return (
- +
diff --git a/packages/studio/src/components/editor/PropertyPanelFlatFooter.tsx b/packages/studio/src/components/editor/PropertyPanelFlatFooter.tsx index 14149a5b16..cf87656260 100644 --- a/packages/studio/src/components/editor/PropertyPanelFlatFooter.tsx +++ b/packages/studio/src/components/editor/PropertyPanelFlatFooter.tsx @@ -40,7 +40,7 @@ export function PropertyPanelFlatFooter({ height="13" viewBox="0 0 16 16" fill="currentColor" - className="text-panel-accent" + className="text-accent-ink" > @@ -57,7 +57,7 @@ export function PropertyPanelFlatFooter({ track("button", "Gesture recording"); onToggleRecording(); }} - className={recording ? "text-panel-danger animate-pulse" : "text-panel-danger"} + className={recording ? "text-danger-ink animate-pulse" : "text-danger-ink"} > {recording ? ( diff --git a/packages/studio/src/components/editor/PropertyPanelFlatHeader.test.tsx b/packages/studio/src/components/editor/PropertyPanelFlatHeader.test.tsx index 51c43ad266..f5722aa3b2 100644 --- a/packages/studio/src/components/editor/PropertyPanelFlatHeader.test.tsx +++ b/packages/studio/src/components/editor/PropertyPanelFlatHeader.test.tsx @@ -38,7 +38,7 @@ describe("PropertyPanelFlatHeader", () => { expect(host.textContent).toContain("Mono Label"); expect(host.textContent).toContain(".mono-label · div"); const icon = host.querySelector('[data-flat-header-icon="true"]'); - expect(icon?.className).toContain("text-panel-accent"); + expect(icon?.className).toContain("text-accent-ink"); act(() => root.unmount()); }); diff --git a/packages/studio/src/components/editor/PropertyPanelFlatHeader.tsx b/packages/studio/src/components/editor/PropertyPanelFlatHeader.tsx index c0ac2c24db..d19ac73fbc 100644 --- a/packages/studio/src/components/editor/PropertyPanelFlatHeader.tsx +++ b/packages/studio/src/components/editor/PropertyPanelFlatHeader.tsx @@ -4,7 +4,7 @@ import { ClipboardList, Film, Square, Type, X } from "../../icons/SystemIcons"; const ICON_BY_KIND = { text: Type, media: Film, other: Square } as const; const ICON_COLOR_BY_KIND = { - text: "text-panel-accent", + text: "text-accent-ink", media: "text-panel-media", other: "text-panel-container", } as const; @@ -92,7 +92,7 @@ export function PropertyPanelFlatHeader({ track("button", "Copy element info"); onCopy(); }} - className={copied ? "text-panel-accent" : undefined} + className={copied ? "text-accent-ink" : undefined} > diff --git a/packages/studio/src/components/editor/SnapToolbar.tsx b/packages/studio/src/components/editor/SnapToolbar.tsx index 267da1171b..e86eed1ff2 100644 --- a/packages/studio/src/components/editor/SnapToolbar.tsx +++ b/packages/studio/src/components/editor/SnapToolbar.tsx @@ -73,7 +73,7 @@ export const SnapToolbar = memo(function SnapToolbar() { type="button" className={`rounded-md p-1.5 transition-colors active:scale-[0.95] ${ motionPathArmed - ? "bg-studio-accent/20 text-studio-accent" + ? "bg-studio-accent/20 text-accent-ink" : "bg-black/40 text-white/60 hover:bg-black/60 hover:text-white/80" }`} onClick={() => setMotionPathArmed(!motionPathArmed)} @@ -99,7 +99,7 @@ export const SnapToolbar = memo(function SnapToolbar() { type="button" className={`rounded-md p-1.5 transition-colors active:scale-[0.95] ${ visible - ? "bg-studio-accent/20 text-studio-accent" + ? "bg-studio-accent/20 text-accent-ink" : "bg-black/40 text-white/60 hover:bg-black/60 hover:text-white/80" }`} onClick={toggle} @@ -115,7 +115,7 @@ export const SnapToolbar = memo(function SnapToolbar() { type="button" className={`rounded-md p-1.5 transition-colors active:scale-[0.95] ${ prefs.snapEnabled - ? "bg-studio-accent/20 text-studio-accent" + ? "bg-studio-accent/20 text-accent-ink" : "bg-black/40 text-white/60 hover:bg-black/60 hover:text-white/80" }`} onClick={toggleSnap} @@ -131,7 +131,7 @@ export const SnapToolbar = memo(function SnapToolbar() { type="button" className={`rounded-md p-1.5 transition-colors active:scale-[0.95] ${ prefs.gridVisible - ? "bg-studio-accent/20 text-studio-accent" + ? "bg-studio-accent/20 text-accent-ink" : "bg-black/40 text-white/60 hover:bg-black/60 hover:text-white/80" }`} onClick={toggleGrid} @@ -164,7 +164,7 @@ export const SnapToolbar = memo(function SnapToolbar() { {gridPopoverOpen && (
-
+
{lutImportError && ( -
+
{lutImportError}
)} diff --git a/packages/studio/src/components/editor/propertyPanelColorGradingSection.tsx b/packages/studio/src/components/editor/propertyPanelColorGradingSection.tsx index 293dde5ac0..bb44f6edc2 100644 --- a/packages/studio/src/components/editor/propertyPanelColorGradingSection.tsx +++ b/packages/studio/src/components/editor/propertyPanelColorGradingSection.tsx @@ -14,11 +14,11 @@ import { useTrackDesignInput } from "../../contexts/DesignPanelInputContext"; function StatusPill({ status }: { status: RuntimeColorGradingStatus }) { const dotClass = status.state === "active" - ? "bg-emerald-400" + ? "bg-accent" : status.state === "pending" - ? "bg-amber-300" + ? "bg-warning-ink" : status.state === "unavailable" - ? "bg-red-400" + ? "bg-danger-ink" : "bg-panel-text-5"; return (
+
{metadata.color.label} source - + SDR preview
-

+

These controls use the current SDR shader preview path. Render may stay HDR-tagged, but this is not true HDR color grading yet.

- {details &&

{details}

} + {details &&

{details}

}
); } @@ -120,7 +120,7 @@ function HoldBeforeButton({ }} className={`flex h-6 w-6 shrink-0 items-center justify-center rounded transition-colors ${ active - ? "bg-studio-accent text-black" + ? "bg-studio-accent text-on-accent" : "text-panel-text-4 hover:bg-panel-hover hover:text-panel-text-1" } disabled:cursor-not-allowed disabled:opacity-40`} title="Hold to show original" diff --git a/packages/studio/src/components/editor/propertyPanelColorGradingSlider.tsx b/packages/studio/src/components/editor/propertyPanelColorGradingSlider.tsx index 9f784b8693..d997569cee 100644 --- a/packages/studio/src/components/editor/propertyPanelColorGradingSlider.tsx +++ b/packages/studio/src/components/editor/propertyPanelColorGradingSlider.tsx @@ -158,7 +158,7 @@ export function ColorGradingSliderControl({ settings.onClick(); }} className={`relative flex h-5 w-5 shrink-0 items-center justify-center rounded transition-colors hover:bg-panel-hover hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40 ${ - settings.active ? "text-studio-accent" : "text-panel-text-5" + settings.active ? "text-accent-ink" : "text-panel-text-5" }`} title={settings.label} > diff --git a/packages/studio/src/components/editor/propertyPanelColorSecondary.tsx b/packages/studio/src/components/editor/propertyPanelColorSecondary.tsx index ff027492cd..a447cad6fd 100644 --- a/packages/studio/src/components/editor/propertyPanelColorSecondary.tsx +++ b/packages/studio/src/components/editor/propertyPanelColorSecondary.tsx @@ -204,13 +204,13 @@ export function PropertyPanelColorSecondary({ setSampling(false); } }} - className="flex min-h-7 items-center gap-1.5 text-[10px] font-medium text-panel-accent hover:text-panel-accent/80 disabled:opacity-50" + className="flex min-h-7 items-center gap-1.5 text-[10px] font-medium text-accent-ink hover:text-accent-ink/80 disabled:opacity-50" > {sampling ? "Capturing frame" : "Sample color from frame"} {captureError && ( -

+

{captureError}

)} @@ -221,7 +221,7 @@ export function PropertyPanelColorSecondary({ type="button" aria-pressed={!showMatte} onClick={() => setShowMatte(false)} - className={!showMatte ? "text-panel-accent" : "text-panel-text-4"} + className={!showMatte ? "text-accent-ink" : "text-panel-text-4"} > Source @@ -229,7 +229,7 @@ export function PropertyPanelColorSecondary({ type="button" aria-pressed={showMatte} onClick={() => setShowMatte(true)} - className={showMatte ? "text-panel-accent" : "text-panel-text-4"} + className={showMatte ? "text-accent-ink" : "text-panel-text-4"} > Selection matte diff --git a/packages/studio/src/components/editor/propertyPanelFill.tsx b/packages/studio/src/components/editor/propertyPanelFill.tsx index 16286e246c..8bb9e998c3 100644 --- a/packages/studio/src/components/editor/propertyPanelFill.tsx +++ b/packages/studio/src/components/editor/propertyPanelFill.tsx @@ -131,7 +131,7 @@ export function ImageFillField({ className={`inline-flex h-7 max-w-full items-center gap-1.5 rounded-lg border border-neutral-700 bg-neutral-950 px-2.5 text-[11px] font-medium text-neutral-300 transition-colors ${ disabled || uploading ? "cursor-not-allowed text-neutral-600" - : "cursor-pointer hover:border-neutral-600 hover:text-white" + : "cursor-pointer hover:border-neutral-600 hover:text-text-0" }`} > @@ -151,7 +151,7 @@ export function ImageFillField({ />
{uploadError && ( -
+
{uploadError}
)} @@ -340,7 +340,7 @@ export function GradientField({ track("toggle", "Repeat gradient"); patch({ repeating: e.target.checked }); }} - className="h-4 w-4 rounded-sm border-neutral-700 bg-neutral-950 text-panel-accent focus:ring-panel-accent" + className="h-4 w-4 rounded-sm border-neutral-700 bg-neutral-950 text-accent-ink focus:ring-panel-accent" /> Repeat @@ -357,7 +357,7 @@ export function GradientField({ })), }); }} - className="inline-flex h-7 items-center gap-1.5 rounded-lg border border-neutral-700 bg-neutral-950 px-2.5 text-[11px] font-medium text-neutral-300 transition-colors hover:border-neutral-600 hover:text-white disabled:cursor-not-allowed disabled:text-neutral-600" + className="inline-flex h-7 items-center gap-1.5 rounded-lg border border-neutral-700 bg-neutral-950 px-2.5 text-[11px] font-medium text-neutral-300 transition-colors hover:border-neutral-600 hover:text-text-0 disabled:cursor-not-allowed disabled:text-neutral-600" > Reverse @@ -442,7 +442,7 @@ export function GradientField({ disabled={disabled || parsed.stops.length >= 6} onClick={() => addStop()} title={parsed.stops.length >= 6 ? "Maximum 6 stops" : "Add a gradient stop"} - className="inline-flex h-7 items-center gap-1.5 rounded-lg border border-neutral-700 bg-neutral-950 px-2.5 text-[11px] font-medium text-neutral-300 transition-colors hover:border-neutral-600 hover:text-white active:scale-[0.98] disabled:cursor-not-allowed disabled:text-neutral-600" + className="inline-flex h-7 items-center gap-1.5 rounded-lg border border-neutral-700 bg-neutral-950 px-2.5 text-[11px] font-medium text-neutral-300 transition-colors hover:border-neutral-600 hover:text-text-0 active:scale-[0.98] disabled:cursor-not-allowed disabled:text-neutral-600" > Add stop @@ -474,7 +474,7 @@ export function GradientField({ type="button" disabled={disabled || parsed.stops.length <= 2} onClick={() => removeStop(index)} - className="mt-[22px] flex h-10 items-center justify-center rounded-lg border border-neutral-700 bg-neutral-950 text-neutral-400 transition-colors hover:border-neutral-600 hover:text-white disabled:cursor-not-allowed disabled:text-neutral-700" + className="mt-[22px] flex h-10 items-center justify-center rounded-lg border border-neutral-700 bg-neutral-950 text-neutral-400 transition-colors hover:border-neutral-600 hover:text-text-0 disabled:cursor-not-allowed disabled:text-text-off" aria-label={`Remove stop ${index + 1}`} > diff --git a/packages/studio/src/components/editor/propertyPanelFlatColorGradingAccessory.tsx b/packages/studio/src/components/editor/propertyPanelFlatColorGradingAccessory.tsx index 57ee807190..0048c7e156 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatColorGradingAccessory.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatColorGradingAccessory.tsx @@ -5,9 +5,9 @@ import { Compare, RotateCcw } from "../../icons/SystemIcons"; import type { ColorGradingControllerState } from "./useColorGradingController"; const STATUS_DOT_CLASS: Record = { - active: "bg-emerald-400", - pending: "bg-amber-300", - unavailable: "bg-red-400", + active: "bg-accent", + pending: "bg-warning-ink", + unavailable: "bg-danger-ink", missing: "bg-panel-text-5", inactive: "bg-panel-text-5", }; diff --git a/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx b/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx index 987bd4ee78..dc17a71c58 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx @@ -81,7 +81,7 @@ describe("FlatColorGradingAccessory", () => { const dot = host.querySelector('[data-flat-grade-status-dot="true"]'); expect(dot).not.toBeNull(); expect(dot?.getAttribute("title")).toBe("Shader active"); - expect(dot?.className).toContain("bg-emerald-400"); + expect(dot?.className).toContain("bg-accent"); act(() => root.unmount()); }); diff --git a/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.tsx b/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.tsx index 58bd3a7a81..419a547c73 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.tsx @@ -70,23 +70,20 @@ function HdrBanner({ metadata }: { metadata: MediaMetadata | null }) { return (
{metadata.color.label} source - + SDR preview
-

+

These controls use the current SDR shader preview path. Render may stay HDR-tagged, but this is not true HDR color grading yet.

{details && ( -

+

{details}

)} @@ -214,7 +211,7 @@ export function FlatColorGradingSection({ @@ -519,7 +516,7 @@ export function FlatColorGradingSection({ track("button", "Apply grade to scope"); onApplyToScope(); }} - className="text-[11px] font-medium text-panel-accent hover:text-panel-accent/80 disabled:cursor-not-allowed disabled:opacity-50" + className="text-[11px] font-medium text-accent-ink hover:text-accent-ink/80 disabled:cursor-not-allowed disabled:opacity-50" > {applyBusy ? "Applying" : "Apply"} diff --git a/packages/studio/src/components/editor/propertyPanelFlatEffectsSection.tsx b/packages/studio/src/components/editor/propertyPanelFlatEffectsSection.tsx index 5b9261ea9e..41fff64c6f 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatEffectsSection.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatEffectsSection.tsx @@ -186,7 +186,7 @@ export function FlatEffectsSection({ track("button", "Customize effect palette"); onCommitColorGrading({ ...grading, palette: fallback }); }} - className="flex min-h-[28px] items-center gap-1 text-[10px] font-medium text-panel-accent hover:text-panel-accent/80" + className="flex min-h-[28px] items-center gap-1 text-[10px] font-medium text-accent-ink hover:text-accent-ink/80" > Custom palette @@ -307,7 +307,7 @@ export function FlatEffectsSection({ type="button" title={`Remove ${selectedEffect.label}`} onClick={() => removeEffect(selectedEffect)} - className="text-panel-text-4 hover:text-red-300" + className="text-panel-text-4 hover:text-danger-ink" > @@ -352,7 +352,7 @@ export function FlatEffectsSection({ data-flat-effects-add-toggle="true" aria-expanded={catalogOpen} onClick={() => setCatalogOpen((open) => !open)} - className="flex min-h-[30px] items-center gap-1 text-[10px] font-medium text-panel-accent hover:text-panel-accent/80" + className="flex min-h-[30px] items-center gap-1 text-[10px] font-medium text-accent-ink hover:text-accent-ink/80" > Add effect @@ -374,7 +374,7 @@ export function FlatEffectsSection({ onClick={() => setCatalogGroup(group.label)} className={`min-h-[25px] min-w-0 truncate bg-panel-bg px-2 text-[9px] ${ catalogGroup === group.label - ? "font-medium text-panel-accent" + ? "font-medium text-accent-ink" : "text-panel-text-4 hover:text-panel-text-1" }`} > diff --git a/packages/studio/src/components/editor/propertyPanelFlatMediaSection.tsx b/packages/studio/src/components/editor/propertyPanelFlatMediaSection.tsx index e23b8600b2..cd38f9aaae 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatMediaSection.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatMediaSection.tsx @@ -200,7 +200,7 @@ export function FlatMediaSection({ data-flat-media-remove-bg="true" disabled={!canRemoveBackground || removeBusy} onClick={() => void runBackgroundRemoval()} - className="flex items-center gap-1 text-[10px] font-medium text-panel-accent disabled:cursor-not-allowed disabled:opacity-50" + className="flex items-center gap-1 text-[10px] font-medium text-accent-ink disabled:cursor-not-allowed disabled:opacity-50" title={ canRemoveBackground ? "Remove background and save a transparent asset" @@ -231,7 +231,7 @@ export function FlatMediaSection({
diff --git a/packages/studio/src/components/editor/propertyPanelFlatMotionSection.tsx b/packages/studio/src/components/editor/propertyPanelFlatMotionSection.tsx index 47f27a71f9..599b5b95f7 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatMotionSection.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatMotionSection.tsx @@ -148,13 +148,13 @@ export function FlatMotionSection({ {showEffects && ( <> {multipleTimelines && ( -

+

This file has multiple GSAP timelines. Animation editing is disabled to prevent data loss — consolidate into a single timeline to enable editing.

)} {unsupportedTimelinePattern && ( -

+

This timeline uses a computed key the editor can't resolve statically.

)} diff --git a/packages/studio/src/components/editor/propertyPanelFlatOverlaysSection.tsx b/packages/studio/src/components/editor/propertyPanelFlatOverlaysSection.tsx index faeca7aff3..d8e417fe4d 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatOverlaysSection.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatOverlaysSection.tsx @@ -45,7 +45,7 @@ export function FlatOverlaysSection({ return
Loading overlays…
; } if (error) { - return
{error}
; + return
{error}
; } const busy = adding !== null; @@ -95,7 +95,7 @@ export function FlatOverlaysSection({ )} diff --git a/packages/studio/src/components/editor/propertyPanelFlatPrimitives.test.tsx b/packages/studio/src/components/editor/propertyPanelFlatPrimitives.test.tsx index d092ee317a..c8999c8a29 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatPrimitives.test.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatPrimitives.test.tsx @@ -60,7 +60,7 @@ describe("FlatRow", () => { />, ); const value = host.querySelector('[data-flat-row-value="true"]'); - expect(value?.className).toContain("text-panel-accent"); + expect(value?.className).toContain("text-accent-ink"); const reset = host.querySelector('[data-flat-row-reset="true"]'); expect(reset).not.toBeNull(); act(() => reset?.dispatchEvent(new MouseEvent("click", { bubbles: true }))); @@ -356,7 +356,7 @@ describe("FlatSlider", () => { const fill = host.querySelector('[data-flat-slider-fill="true"]'); expect(fill?.style.width).toBe("100%"); const knob = host.querySelector('[data-flat-slider-knob="true"]'); - expect(knob?.className).toContain("bg-white"); + expect(knob?.className).toContain("bg-text-0"); act(() => root.unmount()); }); @@ -1172,7 +1172,7 @@ describe("FlatSelectRow", () => { />, ); const select = host.querySelector("select"); - expect(select?.className).toContain("text-panel-accent"); + expect(select?.className).toContain("text-accent-ink"); const reset = host.querySelector('[data-flat-select-reset="true"]'); act(() => reset?.dispatchEvent(new MouseEvent("click", { bubbles: true }))); expect(onReset).toHaveBeenCalledTimes(1); @@ -1364,6 +1364,7 @@ describe("FlatSlider — typed value", () => { const still = host.querySelector('[data-flat-slider-input="true"]'); expect(still).not.toBeNull(); expect(still?.getAttribute("aria-invalid")).toBe("true"); + expect(still?.className).toContain("border-dashed"); act(() => still?.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", bubbles: true }))); expect(host.querySelector('[data-flat-slider-input="true"]')).toBeNull(); expect(host.querySelector('[data-flat-slider-value="true"]')?.textContent).toBe("1x"); diff --git a/packages/studio/src/components/editor/propertyPanelFlatPrimitives.tsx b/packages/studio/src/components/editor/propertyPanelFlatPrimitives.tsx index 80e97b733d..800583b88b 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatPrimitives.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatPrimitives.tsx @@ -537,7 +537,7 @@ export function FlatSlider({
diff --git a/packages/studio/src/components/editor/propertyPanelFlatSliderReadout.tsx b/packages/studio/src/components/editor/propertyPanelFlatSliderReadout.tsx index dc4996ba8d..f280e6504c 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatSliderReadout.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatSliderReadout.tsx @@ -76,7 +76,7 @@ export function FlatSliderReadout({ }} onBlur={() => commit(false)} className={`w-11 shrink-0 rounded-[3px] border bg-panel-surface px-1 text-right font-mono text-[10px] text-panel-text-0 outline-none ${ - invalid ? "border-red-400" : "border-panel-accent" + invalid ? "border-dashed border-danger-ink" : "border-panel-accent" }`} /> ); diff --git a/packages/studio/src/components/editor/propertyPanelFont.tsx b/packages/studio/src/components/editor/propertyPanelFont.tsx index c5b3712883..0f0c99569b 100644 --- a/packages/studio/src/components/editor/propertyPanelFont.tsx +++ b/packages/studio/src/components/editor/propertyPanelFont.tsx @@ -433,7 +433,7 @@ export function FontFamilyField({ type="button" disabled={disabled || loadingLocalFonts} onClick={loadBrowserLocalFonts} - className="rounded-lg border border-neutral-700 bg-neutral-900 px-2.5 text-[10px] font-medium text-neutral-400 transition-colors hover:border-neutral-600 hover:text-neutral-100 disabled:cursor-not-allowed disabled:text-neutral-700" + className="rounded-lg border border-neutral-700 bg-neutral-900 px-2.5 text-[10px] font-medium text-neutral-400 transition-colors hover:border-neutral-600 hover:text-neutral-100 disabled:cursor-not-allowed disabled:text-text-off" > {loadingLocalFonts ? "..." : "Local"} @@ -442,7 +442,7 @@ export function FontFamilyField({ type="button" disabled={disabled || importingFonts || !onImportFonts} onClick={() => fontInputRef.current?.click()} - className="rounded-lg border border-neutral-700 bg-neutral-900 px-2.5 text-[10px] font-medium text-neutral-400 transition-colors hover:border-neutral-600 hover:text-neutral-100 disabled:cursor-not-allowed disabled:text-neutral-700" + className="rounded-lg border border-neutral-700 bg-neutral-900 px-2.5 text-[10px] font-medium text-neutral-400 transition-colors hover:border-neutral-600 hover:text-neutral-100 disabled:cursor-not-allowed disabled:text-text-off" > {importingFonts ? "..." : "Import"} diff --git a/packages/studio/src/components/editor/propertyPanelFxCarveModule.tsx b/packages/studio/src/components/editor/propertyPanelFxCarveModule.tsx index 4a61df2ca7..1357e6e32c 100644 --- a/packages/studio/src/components/editor/propertyPanelFxCarveModule.tsx +++ b/packages/studio/src/components/editor/propertyPanelFxCarveModule.tsx @@ -111,7 +111,7 @@ function FxCarveMember({ {/* The lane is where an automated value comes from, and where it is edited — saying so is the difference between a stale readout and a pointer to the thing that owns it. */} - {automated ? A : null} + {automated ? A : null} ); })} diff --git a/packages/studio/src/components/editor/propertyPanelFxControls.tsx b/packages/studio/src/components/editor/propertyPanelFxControls.tsx index 5bbc926e68..be1bfbc508 100644 --- a/packages/studio/src/components/editor/propertyPanelFxControls.tsx +++ b/packages/studio/src/components/editor/propertyPanelFxControls.tsx @@ -90,7 +90,7 @@ export function AutomationToggle({ type="button" className={`hf-fx-automate w-[16px] shrink-0 rounded-[3px] border font-mono text-[9px] leading-none ${ automated - ? "border-panel-accent text-panel-accent" + ? "border-panel-accent text-accent-ink" : "border-panel-border-input text-panel-text-2 hover:text-panel-text-0" }`} aria-pressed={automated} @@ -237,7 +237,7 @@ export function FxParamRow({ {/* See the enum row above for why the name wraps instead of truncating. */} {param.label} diff --git a/packages/studio/src/components/editor/propertyPanelFxEqModule.tsx b/packages/studio/src/components/editor/propertyPanelFxEqModule.tsx index cb04c68ddf..d76aa0d26b 100644 --- a/packages/studio/src/components/editor/propertyPanelFxEqModule.tsx +++ b/packages/studio/src/components/editor/propertyPanelFxEqModule.tsx @@ -115,7 +115,7 @@ function Fader({ {moved ? shown(value) : "0"} @@ -160,7 +160,7 @@ export function FxEqModule({ {bands.length}-band @@ -474,7 +474,7 @@ export function TextSection({ if (nextKey) setActiveTextFieldKey(nextKey); }); }} - className="inline-flex h-7 max-w-full items-center gap-1.5 rounded-lg border border-neutral-700 bg-neutral-950 px-2.5 text-[11px] font-medium text-neutral-300 transition-colors hover:border-neutral-600 hover:text-white" + className="inline-flex h-7 max-w-full items-center gap-1.5 rounded-lg border border-neutral-700 bg-neutral-950 px-2.5 text-[11px] font-medium text-neutral-300 transition-colors hover:border-neutral-600 hover:text-text-0" > Add text diff --git a/packages/studio/src/components/editor/propertyPanelValueTier.test.ts b/packages/studio/src/components/editor/propertyPanelValueTier.test.ts index 80e4c22048..b4b95f0c64 100644 --- a/packages/studio/src/components/editor/propertyPanelValueTier.test.ts +++ b/packages/studio/src/components/editor/propertyPanelValueTier.test.ts @@ -27,6 +27,6 @@ describe("value tier class maps", () => { expect(VALUE_TIER_LABEL_CLASS[tier]).toBeTruthy(); expect(VALUE_TIER_VALUE_CLASS[tier]).toBeTruthy(); } - expect(VALUE_TIER_VALUE_CLASS.explicitCustom).toBe("text-panel-accent"); + expect(VALUE_TIER_VALUE_CLASS.explicitCustom).toBe("text-accent-ink"); }); }); diff --git a/packages/studio/src/components/editor/propertyPanelValueTier.ts b/packages/studio/src/components/editor/propertyPanelValueTier.ts index 0e92f8b04d..5bac54e117 100644 --- a/packages/studio/src/components/editor/propertyPanelValueTier.ts +++ b/packages/studio/src/components/editor/propertyPanelValueTier.ts @@ -24,5 +24,5 @@ export const VALUE_TIER_LABEL_CLASS: Record = { export const VALUE_TIER_VALUE_CLASS: Record = { default: "text-panel-text-3", explicitDefault: "text-panel-text-0", - explicitCustom: "text-panel-accent", + explicitCustom: "text-accent-ink", }; diff --git a/packages/studio/src/components/feedback/StudioFeedbackCard.tsx b/packages/studio/src/components/feedback/StudioFeedbackCard.tsx index a3a98251f6..9ed8f7ad48 100644 --- a/packages/studio/src/components/feedback/StudioFeedbackCard.tsx +++ b/packages/studio/src/components/feedback/StudioFeedbackCard.tsx @@ -1,4 +1,5 @@ import { memo, useState, useCallback, useRef, useEffect } from "react"; +import { toastSurface } from "../StudioToast"; import { trackStudioFeedback, trackStudioFeedbackShown, @@ -212,24 +213,11 @@ export const StudioFeedbackCard = memo(function StudioFeedbackCard() { }} >

{step === "thanks" ? "Thanks, that helps." : title}

@@ -243,7 +231,7 @@ export const StudioFeedbackCard = memo(function StudioFeedbackCard() { if (req) trackStudioFeedbackInterviewClick({ reason: req.reason }); close("sent"); }} - className="h-6 shrink-0 whitespace-nowrap rounded-full border border-white/20 bg-white/8 px-2.5 text-[11px] leading-6 text-neutral-100 transition-[background-color,border-color,transform] duration-150 ease-out hover:border-white/35 hover:bg-white/16 active:scale-[0.97] motion-reduce:transition-none" + className="h-6 shrink-0 whitespace-nowrap rounded-full border border-border-strong bg-hover px-2.5 text-[11px] leading-6 text-neutral-100 transition-[background-color,border-color,transform] duration-150 ease-out hover:border-text-muted hover:bg-press active:scale-[0.97] motion-reduce:transition-none" > Talk to us, 30 min @@ -251,7 +239,7 @@ export const StudioFeedbackCard = memo(function StudioFeedbackCard() { diff --git a/packages/studio/src/components/nle/AssetPreviewOverlay.tsx b/packages/studio/src/components/nle/AssetPreviewOverlay.tsx index a951454102..3b1c35cbbf 100644 --- a/packages/studio/src/components/nle/AssetPreviewOverlay.tsx +++ b/packages/studio/src/components/nle/AssetPreviewOverlay.tsx @@ -142,7 +142,7 @@ export function AssetPreviewOverlay() { > {/* Close button */} diff --git a/packages/studio/src/components/nle/PreviewPane.tsx b/packages/studio/src/components/nle/PreviewPane.tsx index 775a508081..78d78a6fac 100644 --- a/packages/studio/src/components/nle/PreviewPane.tsx +++ b/packages/studio/src/components/nle/PreviewPane.tsx @@ -116,9 +116,9 @@ export function PreviewPane({
diff --git a/packages/studio/src/components/nle/PreviewZoomOverlay.tsx b/packages/studio/src/components/nle/PreviewZoomOverlay.tsx index 41bdcf362d..1dcc520780 100644 --- a/packages/studio/src/components/nle/PreviewZoomOverlay.tsx +++ b/packages/studio/src/components/nle/PreviewZoomOverlay.tsx @@ -85,7 +85,7 @@ export function PreviewZoomOverlay({ diff --git a/packages/studio/src/components/panels/SlideshowSubPanels.tsx b/packages/studio/src/components/panels/SlideshowSubPanels.tsx index f7f3d9d1fe..fda688f18b 100644 --- a/packages/studio/src/components/panels/SlideshowSubPanels.tsx +++ b/packages/studio/src/components/panels/SlideshowSubPanels.tsx @@ -72,9 +72,7 @@ export function SlideList({ tabIndex={0} aria-pressed={isSelected} className={`flex items-center gap-2 px-3 py-1.5 rounded cursor-pointer text-[11px] transition-colors ${ - isSelected - ? "bg-studio-accent/20 text-white" - : "hover:bg-neutral-800/60 text-neutral-300" + isSelected ? "bg-on text-text-0" : "hover:bg-neutral-800/60 text-neutral-300" }`} onClick={() => onSelect(scene.id)} onKeyDown={(e) => { @@ -100,7 +98,7 @@ export function SlideList({ aria-label="Move slide up" title="Move up" disabled={slideIndex <= 0} - className="px-1 py-0.5 text-[10px] text-neutral-400 enabled:hover:text-white enabled:active:scale-[0.95] disabled:opacity-30 disabled:cursor-not-allowed" + className="px-1 py-0.5 text-[10px] text-neutral-400 enabled:hover:text-text-0 enabled:active:scale-[0.95] disabled:opacity-30 disabled:cursor-not-allowed" onClick={(e) => { e.stopPropagation(); onReorder(scene.id, "up"); @@ -113,7 +111,7 @@ export function SlideList({ aria-label="Move slide down" title="Move down" disabled={slideIndex === slides.length - 1} - className="px-1 py-0.5 text-[10px] text-neutral-400 enabled:hover:text-white enabled:active:scale-[0.95] disabled:opacity-30 disabled:cursor-not-allowed" + className="px-1 py-0.5 text-[10px] text-neutral-400 enabled:hover:text-text-0 enabled:active:scale-[0.95] disabled:opacity-30 disabled:cursor-not-allowed" onClick={(e) => { e.stopPropagation(); onReorder(scene.id, "down"); @@ -162,7 +160,7 @@ export function SlideInspector({