@hyperframes/studio 0.8.104 → 0.8.106

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/dist/assets/{hyperframes-player-DAoaajuM.js → hyperframes-player-dl6K6ABp.js} +1 -1
  2. package/dist/assets/index-CMiYVGv5.js +399 -0
  3. package/dist/assets/{index-WV9LroAD.js → index-DKT5NF1Y.js} +1 -1
  4. package/dist/assets/index-lrT-gfHa.css +1 -0
  5. package/dist/assets/{index-BDK4rTMb.js → index-ooaZZhKf.js} +1 -1
  6. package/dist/{chunk-62DDWOSH.js → chunk-EEXSYY5F.js} +19 -18
  7. package/dist/chunk-EEXSYY5F.js.map +1 -0
  8. package/dist/{chunk-HIGHVWYD.js → chunk-ICTZJPQQ.js} +3 -2
  9. package/dist/chunk-ICTZJPQQ.js.map +1 -0
  10. package/dist/{chunk-6GAO75IU.js → chunk-NCUBAVGK.js} +10 -8
  11. package/dist/chunk-NCUBAVGK.js.map +1 -0
  12. package/dist/index.css +6 -1
  13. package/dist/index.css.map +1 -1
  14. package/dist/index.d.ts +15 -4
  15. package/dist/index.html +11 -2
  16. package/dist/index.js +10391 -9836
  17. package/dist/index.js.map +1 -1
  18. package/dist/player.js +2 -2
  19. package/dist/styles/tailwind-preset.js.map +1 -1
  20. package/dist/styles.css +1 -1
  21. package/dist/ui.js +2 -2
  22. package/package.json +9 -9
  23. package/src/captions/components/CaptionAnimationPanel.tsx +2 -2
  24. package/src/captions/components/CaptionPropertyPanel.tsx +2 -2
  25. package/src/captions/components/CaptionTimeline.tsx +3 -3
  26. package/src/captions/keyboard.test.ts +6 -0
  27. package/src/captions/keyboard.ts +2 -1
  28. package/src/components/AskAgentModal.tsx +1 -1
  29. package/src/components/LintModal.tsx +35 -35
  30. package/src/components/StudioErrorBoundary.tsx +1 -1
  31. package/src/components/StudioHeader.dom.test.tsx +35 -13
  32. package/src/components/StudioHeader.tsx +6 -2
  33. package/src/components/StudioToast.tsx +7 -16
  34. package/src/components/ThemeToggle.tsx +99 -0
  35. package/src/components/TimelineToolPicker.tsx +1 -1
  36. package/src/components/TimelineToolbar.tsx +11 -11
  37. package/src/components/dock/Dock.tsx +1 -1
  38. package/src/components/dock/dock.css +9 -1
  39. package/src/components/editor/AnimationCard.tsx +5 -5
  40. package/src/components/editor/AnimationCardParts.tsx +1 -1
  41. package/src/components/editor/BlockParamsPanel.tsx +3 -3
  42. package/src/components/editor/BorderRadiusEditor.tsx +8 -15
  43. package/src/components/editor/CanvasContextMenu.tsx +1 -1
  44. package/src/components/editor/ComputedTweenNotice.tsx +1 -1
  45. package/src/components/editor/DomEditOverlay.tsx +37 -5
  46. package/src/components/editor/DomEditRotateHandle.tsx +1 -1
  47. package/src/components/editor/EaseCurveSection.tsx +14 -19
  48. package/src/components/editor/EaseModeControls.tsx +2 -2
  49. package/src/components/editor/EaseParamFields.tsx +8 -8
  50. package/src/components/editor/FileTree.tsx +1 -1
  51. package/src/components/editor/FileTreeIcons.tsx +47 -26
  52. package/src/components/editor/FileTreeNodes.tsx +11 -8
  53. package/src/components/editor/GestureRecordControl.tsx +3 -3
  54. package/src/components/editor/GsapAddAnimationControl.tsx +1 -1
  55. package/src/components/editor/GsapAnimationSection.tsx +2 -2
  56. package/src/components/editor/InlineTextToolbar.tsx +4 -4
  57. package/src/components/editor/InspectorHeaderActions.tsx +1 -1
  58. package/src/components/editor/KeyframeDiamond.tsx +6 -4
  59. package/src/components/editor/KeyframeNavigation.tsx +4 -4
  60. package/src/components/editor/LayersPanel.tsx +3 -3
  61. package/src/components/editor/PromotableControl.tsx +2 -2
  62. package/src/components/editor/PropertyPanelEmptyState.tsx +4 -4
  63. package/src/components/editor/PropertyPanelFlatFooter.tsx +2 -2
  64. package/src/components/editor/PropertyPanelFlatHeader.test.tsx +1 -1
  65. package/src/components/editor/PropertyPanelFlatHeader.tsx +2 -2
  66. package/src/components/editor/SnapToolbar.tsx +5 -5
  67. package/src/components/editor/SourceEditor.tsx +2 -6
  68. package/src/components/editor/TimelineFxPopover.tsx +4 -4
  69. package/src/components/editor/Transform3DCube.tsx +1 -1
  70. package/src/components/editor/anchoredResizeReleaseShift.test.ts +0 -84
  71. package/src/components/editor/domEditOverlayStartGesture.ts +127 -115
  72. package/src/components/editor/easeCurveSvg.tsx +1 -1
  73. package/src/components/editor/gestureHoldsPreviewReload.test.tsx +275 -0
  74. package/src/components/editor/manualEditsDom.ts +36 -3
  75. package/src/components/editor/manualEditsDomPatches.test.ts +2 -86
  76. package/src/components/editor/manualEditsDomPatches.ts +0 -30
  77. package/src/components/editor/manualEditsSnapshot.ts +1 -0
  78. package/src/components/editor/marqueeCommit.test.tsx +69 -1
  79. package/src/components/editor/marqueeCommit.ts +6 -1
  80. package/src/components/editor/overlayFrameLoop.test.ts +27 -0
  81. package/src/components/editor/overlayFrameLoop.ts +21 -0
  82. package/src/components/editor/persistSeam.integration.test.ts +0 -18
  83. package/src/components/editor/plainTranslate.ts +34 -3
  84. package/src/components/editor/propertyPanelColorCurveGraph.tsx +2 -2
  85. package/src/components/editor/propertyPanelColorGradingControls.tsx +1 -1
  86. package/src/components/editor/propertyPanelColorGradingSection.tsx +8 -8
  87. package/src/components/editor/propertyPanelColorGradingSlider.tsx +1 -1
  88. package/src/components/editor/propertyPanelColorSecondary.tsx +4 -4
  89. package/src/components/editor/propertyPanelFill.tsx +6 -6
  90. package/src/components/editor/propertyPanelFlatColorGradingAccessory.tsx +3 -3
  91. package/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx +1 -1
  92. package/src/components/editor/propertyPanelFlatColorGradingSection.tsx +6 -9
  93. package/src/components/editor/propertyPanelFlatEffectsSection.tsx +4 -4
  94. package/src/components/editor/propertyPanelFlatMediaSection.tsx +2 -2
  95. package/src/components/editor/propertyPanelFlatMotionSection.tsx +2 -2
  96. package/src/components/editor/propertyPanelFlatOverlaysSection.tsx +2 -2
  97. package/src/components/editor/propertyPanelFlatPrimitives.test.tsx +4 -3
  98. package/src/components/editor/propertyPanelFlatPrimitives.tsx +1 -1
  99. package/src/components/editor/propertyPanelFlatSliderReadout.tsx +1 -1
  100. package/src/components/editor/propertyPanelFont.test.tsx +80 -3
  101. package/src/components/editor/propertyPanelFont.tsx +71 -48
  102. package/src/components/editor/propertyPanelFxCarveModule.tsx +1 -1
  103. package/src/components/editor/propertyPanelFxControls.tsx +2 -2
  104. package/src/components/editor/propertyPanelFxEqModule.tsx +2 -2
  105. package/src/components/editor/propertyPanelFxNodeRow.tsx +1 -1
  106. package/src/components/editor/propertyPanelFxPresetRun.tsx +1 -1
  107. package/src/components/editor/propertyPanelHelpers.ts +1 -2
  108. package/src/components/editor/propertyPanelMediaSection.tsx +1 -1
  109. package/src/components/editor/propertyPanelPrimitives.tsx +1 -1
  110. package/src/components/editor/propertyPanelSections.tsx +2 -2
  111. package/src/components/editor/propertyPanelValueTier.test.ts +1 -1
  112. package/src/components/editor/propertyPanelValueTier.ts +1 -1
  113. package/src/components/editor/studioCodeTheme.test.ts +20 -0
  114. package/src/components/editor/studioCodeTheme.ts +99 -0
  115. package/src/components/editor/useDomEditNudge.test.tsx +31 -0
  116. package/src/components/editor/useDomEditNudge.ts +2 -2
  117. package/src/components/editor/useInlineTextEditing.tsx +1 -1
  118. package/src/components/editor/useMotionPathData.test.tsx +64 -0
  119. package/src/components/editor/useMotionPathData.ts +5 -1
  120. package/src/components/feedback/StudioFeedbackCard.tsx +11 -23
  121. package/src/components/nle/AssetPreviewOverlay.tsx +1 -1
  122. package/src/components/nle/AudioMeterStrip.tsx +2 -2
  123. package/src/components/nle/NLEPreview.test.ts +8 -4
  124. package/src/components/nle/PreviewOverlays.tsx +1 -1
  125. package/src/components/nle/PreviewPane.tsx +3 -3
  126. package/src/components/nle/PreviewPoster.tsx +29 -1
  127. package/src/components/nle/PreviewZoomOverlay.tsx +1 -1
  128. package/src/components/panels/SlideshowPanel.tsx +4 -4
  129. package/src/components/panels/SlideshowSubPanels.tsx +19 -21
  130. package/src/components/panels/VariablesBindElement.tsx +1 -1
  131. package/src/components/panels/VariablesDeclarationForm.tsx +1 -1
  132. package/src/components/panels/VariablesPanel.tsx +4 -6
  133. package/src/components/panels/VariablesRowAction.tsx +1 -1
  134. package/src/components/renders/FfmpegRequiredNotice.tsx +4 -4
  135. package/src/components/renders/RenderQueue.tsx +2 -2
  136. package/src/components/renders/RenderQueueItem.tsx +3 -3
  137. package/src/components/sidebar/AssetCard.tsx +3 -3
  138. package/src/components/sidebar/AssetContextMenu.tsx +7 -7
  139. package/src/components/sidebar/AssetsTab.tsx +6 -6
  140. package/src/components/sidebar/AudioRow.tsx +5 -3
  141. package/src/components/sidebar/BlocksTab.tsx +6 -6
  142. package/src/components/sidebar/CompositionsTab.drag.test.tsx +3 -69
  143. package/src/components/sidebar/CompositionsTab.rootBadge.test.tsx +4 -50
  144. package/src/components/sidebar/CompositionsTab.thumbnails.test.tsx +256 -0
  145. package/src/components/sidebar/CompositionsTab.tsx +65 -12
  146. package/src/components/sidebar/PromptPreviewModal.tsx +3 -3
  147. package/src/components/sidebar/SidebarLintButton.tsx +3 -3
  148. package/src/components/sidebar/compositionsTabTestUtils.tsx +39 -0
  149. package/src/components/timelineToolbarStyles.ts +3 -3
  150. package/src/components/ui/Button.tsx +4 -4
  151. package/src/components/ui/HyperframesLogo.tsx +19 -18
  152. package/src/components/ui/Input.tsx +1 -1
  153. package/src/components/ui/Menu.tsx +4 -2
  154. package/src/hooks/appHotkeysDispatch.ts +2 -1
  155. package/src/hooks/elementOffsetStager.ts +8 -7
  156. package/src/hooks/previewPromotionSelection.test.tsx +180 -0
  157. package/src/hooks/useAppHotkeys.test.ts +32 -0
  158. package/src/hooks/useAppHotkeys.ts +1 -2
  159. package/src/hooks/useClipboard.source.test.ts +141 -23
  160. package/src/hooks/useClipboard.ts +154 -123
  161. package/src/hooks/useDomEditCommits.ts +0 -3
  162. package/src/hooks/useDomEditSession.test.tsx +76 -72
  163. package/src/hooks/useDomEditSession.ts +7 -43
  164. package/src/hooks/useDomGeometryCommit.test.tsx +4 -1
  165. package/src/hooks/useDomGeometryCommit.ts +0 -1
  166. package/src/hooks/useDomGeometryCommits.test.tsx +177 -32
  167. package/src/hooks/useDomGeometryCommits.ts +4 -49
  168. package/src/hooks/useGsapTweenCache.ts +1 -2
  169. package/src/hooks/useInlineTextEdit.ts +16 -2
  170. package/src/hooks/useLivePreviewIframe.ts +18 -0
  171. package/src/hooks/usePreviewPersistence.history.test.tsx +47 -0
  172. package/src/hooks/usePreviewPersistence.ts +2 -5
  173. package/src/hooks/useRenderClipContent.ts +3 -3
  174. package/src/index.ts +2 -0
  175. package/src/livePreviewIframeExport.test.tsx +33 -0
  176. package/src/main.tsx +4 -1
  177. package/src/player/components/AudioWaveform.test.tsx +36 -15
  178. package/src/player/components/AudioWaveform.tsx +24 -7
  179. package/src/player/components/ClipContextMenu.tsx +1 -1
  180. package/src/player/components/CompositionThumbnail.tsx +23 -11
  181. package/src/player/components/EditModal.tsx +6 -6
  182. package/src/player/components/KeyframeDiamondContextMenu.tsx +2 -2
  183. package/src/player/components/PlayerControls.tsx +3 -3
  184. package/src/player/components/ShortcutsPanel.tsx +8 -20
  185. package/src/player/components/TimelineCanvas.tsx +1 -1
  186. package/src/player/components/TimelineClip.test.tsx +38 -0
  187. package/src/player/components/TimelineClip.tsx +16 -8
  188. package/src/player/components/TimelineClipFades.test.tsx +376 -74
  189. package/src/player/components/TimelineClipFades.tsx +368 -137
  190. package/src/player/components/TimelineEmptyState.tsx +2 -2
  191. package/src/player/components/TimelineLanes.tsx +0 -1
  192. package/src/player/components/TrackClipCount.tsx +1 -1
  193. package/src/player/components/VolumeControl.tsx +1 -1
  194. package/src/player/components/timelineClipChildren.tsx +1 -1
  195. package/src/player/components/timelineMotionStyles.test.ts +6 -22
  196. package/src/player/components/trackHeaderLabelRows.tsx +16 -16
  197. package/src/player/components/useTimelineKeyboardActor.ts +1 -1
  198. package/src/player/hooks/timelinePlayerTestHarness.ts +27 -0
  199. package/src/player/hooks/useShadowPreviewReload.ts +72 -5
  200. package/src/player/lib/automationStoreSync.test.ts +18 -1
  201. package/src/player/lib/automationStoreSync.ts +29 -17
  202. package/src/player/lib/thumbnailScheduler.ts +4 -5
  203. package/src/player/lib/timelineViewportBudgets.test.ts +1 -1
  204. package/src/player/lib/timelineViewportBudgets.ts +1 -1
  205. package/src/player/sceneSwap.ts +18 -0
  206. package/src/styles/components.css +26 -0
  207. package/src/styles/hex-baseline.json +7 -19
  208. package/src/styles/studio.css +4 -7
  209. package/src/styles/tailwind-preset.shared.js +2 -1
  210. package/src/styles/theme.css +214 -147
  211. package/src/styles/theme.test.ts +13 -9
  212. package/src/utils/blockCategories.ts +41 -9
  213. package/src/utils/clipboardPayload.ts +12 -1
  214. package/src/utils/studioPendingEdits.ts +21 -0
  215. package/src/utils/studioTheme.test.tsx +141 -0
  216. package/src/utils/studioTheme.ts +34 -0
  217. package/src/utils/studioUiPreferences.ts +3 -0
  218. package/src/utils/typingTarget.test.ts +8 -1
  219. package/src/utils/typingTarget.ts +5 -0
  220. package/dist/assets/index-CQN3M56N.css +0 -1
  221. package/dist/assets/index-De2aO4XM.js +0 -399
  222. package/dist/chunk-62DDWOSH.js.map +0 -1
  223. package/dist/chunk-6GAO75IU.js.map +0 -1
  224. package/dist/chunk-HIGHVWYD.js.map +0 -1
  225. package/src/hooks/gsapTargetCache.ts +0 -62
@@ -253,6 +253,32 @@
253
253
  border-radius: var(--timeline-clip-audio-radius);
254
254
  }
255
255
 
256
+ .timeline-fade-tab {
257
+ position: absolute;
258
+ border-radius: 2px;
259
+ background: var(--timeline-fade-dot);
260
+ box-shadow:
261
+ 0 0 0 1px var(--timeline-fade-dot-ring),
262
+ 0 1px 3px var(--timeline-fade-dot-ring);
263
+ transition: transform 140ms cubic-bezier(0.23, 1, 0.32, 1);
264
+ }
265
+
266
+ .timeline-fade-handle:hover .timeline-fade-tab,
267
+ .timeline-fade-handle[data-dragging] .timeline-fade-tab {
268
+ transform: scaleY(1.2);
269
+ }
270
+
271
+ .timeline-fade-handle:focus-visible .timeline-fade-tab {
272
+ outline: 2px solid var(--timeline-accent);
273
+ outline-offset: 2px;
274
+ }
275
+
276
+ @media (prefers-reduced-motion: reduce) {
277
+ .timeline-fade-tab {
278
+ transition: none;
279
+ }
280
+ }
281
+
256
282
  .timeline-clip.is-audio.is-hovered {
257
283
  background-color: var(--timeline-clip-audio-bg-hover);
258
284
  }
@@ -1,41 +1,32 @@
1
1
  {
2
- "total": 297,
2
+ "total": 226,
3
3
  "files": {
4
4
  "packages/studio/src/captions/components/CaptionOverlay.tsx": 5,
5
5
  "packages/studio/src/captions/components/CaptionTimeline.tsx": 11,
6
6
  "packages/studio/src/captions/generator.ts": 1,
7
7
  "packages/studio/src/captions/parser.ts": 1,
8
8
  "packages/studio/src/captions/types.ts": 4,
9
- "packages/studio/src/components/StudioToast.tsx": 10,
10
- "packages/studio/src/components/TimelineToolbar.tsx": 2,
9
+ "packages/studio/src/components/TimelineToolbar.tsx": 1,
11
10
  "packages/studio/src/components/editor/ArcPathControls.tsx": 1,
12
- "packages/studio/src/components/editor/BorderRadiusEditor.tsx": 10,
11
+ "packages/studio/src/components/editor/BorderRadiusEditor.tsx": 2,
13
12
  "packages/studio/src/components/editor/DomEditCropHandles.tsx": 1,
14
- "packages/studio/src/components/editor/EaseCurveSection.tsx": 2,
15
- "packages/studio/src/components/editor/FileTree.tsx": 2,
16
- "packages/studio/src/components/editor/FileTreeIcons.tsx": 17,
17
- "packages/studio/src/components/editor/FileTreeNodes.tsx": 4,
13
+ "packages/studio/src/components/editor/FileTreeIcons.tsx": 7,
18
14
  "packages/studio/src/components/editor/GestureTrailOverlay.tsx": 1,
19
15
  "packages/studio/src/components/editor/GridOverlay.tsx": 4,
20
- "packages/studio/src/components/editor/InlineTextToolbar.tsx": 2,
21
- "packages/studio/src/components/editor/KeyframeDiamond.tsx": 2,
22
- "packages/studio/src/components/editor/KeyframeNavigation.tsx": 2,
16
+ "packages/studio/src/components/editor/InlineTextToolbar.tsx": 1,
23
17
  "packages/studio/src/components/editor/MotionPathNode.tsx": 5,
24
18
  "packages/studio/src/components/editor/SnapGuideOverlay.tsx": 3,
25
- "packages/studio/src/components/editor/TimelineFxPopover.tsx": 1,
26
- "packages/studio/src/components/editor/Transform3DCube.tsx": 6,
19
+ "packages/studio/src/components/editor/Transform3DCube.tsx": 5,
27
20
  "packages/studio/src/components/editor/colorValue.ts": 3,
28
21
  "packages/studio/src/components/editor/domEditingElement.ts": 1,
29
- "packages/studio/src/components/editor/easeCurveSvg.tsx": 2,
30
22
  "packages/studio/src/components/editor/gradientValue.ts": 6,
31
23
  "packages/studio/src/components/editor/panelTokens.ts": 4,
32
24
  "packages/studio/src/components/editor/propertyPanelColor.tsx": 7,
33
- "packages/studio/src/components/editor/propertyPanelColorCurveGraph.tsx": 17,
25
+ "packages/studio/src/components/editor/propertyPanelColorCurveGraph.tsx": 16,
34
26
  "packages/studio/src/components/editor/propertyPanelColorScopes.tsx": 8,
35
27
  "packages/studio/src/components/editor/propertyPanelColorWheels.tsx": 8,
36
28
  "packages/studio/src/components/editor/propertyPanelFlatEffectsSection.tsx": 5,
37
29
  "packages/studio/src/components/editor/propertyPanelFlatStyleSections.tsx": 2,
38
- "packages/studio/src/components/editor/propertyPanelFxCarveModule.tsx": 1,
39
30
  "packages/studio/src/components/editor/propertyPanelFxFamily.ts": 1,
40
31
  "packages/studio/src/components/editor/propertyPanelFxPresetStyle.ts": 24,
41
32
  "packages/studio/src/components/editor/propertyPanelHelpers.ts": 4,
@@ -43,7 +34,6 @@
43
34
  "packages/studio/src/components/editor/propertyPanelSections.tsx": 1,
44
35
  "packages/studio/src/components/editor/propertyPanelStyleSections.tsx": 3,
45
36
  "packages/studio/src/components/editor/transform3dProjection.ts": 3,
46
- "packages/studio/src/components/feedback/StudioFeedbackCard.tsx": 10,
47
37
  "packages/studio/src/components/panels/VariablesBindElement.tsx": 2,
48
38
  "packages/studio/src/components/panels/VariablesValueControls.tsx": 2,
49
39
  "packages/studio/src/components/sidebar/AudioRow.tsx": 2,
@@ -54,9 +44,7 @@
54
44
  "packages/studio/src/player/components/CompositionThumbnail.tsx": 2,
55
45
  "packages/studio/src/player/components/Player.tsx": 2,
56
46
  "packages/studio/src/player/components/PlayerControls.tsx": 1,
57
- "packages/studio/src/player/components/ShortcutsPanel.tsx": 7,
58
47
  "packages/studio/src/player/components/SpeedMenu.tsx": 2,
59
- "packages/studio/src/player/components/trackHeaderLabelRows.tsx": 6,
60
48
  "packages/studio/src/styles/components.css": 48,
61
49
  "packages/studio/src/telemetry/client.ts": 1,
62
50
  "packages/studio/src/webmcp/webmcpTestUtils.ts": 1
@@ -16,13 +16,10 @@
16
16
  }
17
17
  }
18
18
 
19
- /*
20
- * Studio is a dark-only UI — pin the user-agent color scheme to dark so that
21
- * browser-native chrome (scrollbars, form controls, focus rings) picks the
22
- * matching palette instead of defaulting to light against our #0a0a0a body.
23
- */
24
- :root {
25
- color-scheme: dark;
19
+ [data-theme-reveal]::view-transition-old(root),
20
+ [data-theme-reveal]::view-transition-new(root) {
21
+ animation: none;
22
+ mix-blend-mode: normal;
26
23
  }
27
24
 
28
25
  body {
@@ -1,6 +1,7 @@
1
1
  /** @deprecated Superseded by `src/styles/theme.css` (published as
2
2
  * `@hyperframes/studio/theme.css`); kept one more major for Tailwind v3
3
- * consumers. Values are byte-identical; theme.test.ts fails if they drift. */
3
+ * consumers, frozen at the colours before Graphite (theme.css is light-dark() and colour-mix
4
+ * now, which v3 cannot read). */
4
5
  const studioPreset = {
5
6
  theme: {
6
7
  extend: {
@@ -5,72 +5,120 @@
5
5
  @theme static {
6
6
  /* ---------------------------------------------------------------- colour */
7
7
 
8
- /* Background levels, darkest (app shell) to lightest (inset panel body). */
9
- --color-bg-0: #0a0a0a;
10
- --color-bg-1: #0c0c0e;
11
- --color-bg-2: #121214;
12
-
13
- /* Raised surfaces. */
14
- --color-surface: #18181b;
15
- --color-surface-alt: #141414;
16
- --color-input: #161618;
17
- --color-hover: #27272a;
18
-
19
- /* Separators, weakest to strongest. */
20
- --color-hairline: #1a1a1c;
21
- --color-border: #1e1e1e;
22
- --color-border-input: #27272a;
23
- --color-border-strong: #262626;
24
-
25
- /* Text, brightest (0) to dimmest (5). */
26
- --color-text-0: #fafafa;
27
- --color-text-1: #e4e4e7;
28
- --color-text-2: #a1a1aa;
29
- --color-text-3: #71717a;
30
- --color-text-4: #52525b;
31
- --color-text-5: #3f3f46;
32
- --color-text-body: #e5e5e5;
33
- --color-text-muted: #737373;
8
+ /* Graphite. Each colour is `light-dark(light, dark)`, resolved where it is painted, so the
9
+ * colour-scheme decides: Studio is dark, `data-theme="paper"` makes it light, and the player
10
+ * keeps a dark scheme in both. Text clears 4.5:1 and icons 3:1 on every surface below. */
11
+
12
+ /* Surfaces: the app shell first, menus and popovers last. */
13
+ --color-bg-0: light-dark(oklch(0.955 0.005 70), oklch(0.235 0.007 70));
14
+ --color-bg-1: var(--color-bg-0);
15
+ --color-bg-2: var(--color-surface);
16
+ --color-input: light-dark(oklch(1 0 70), oklch(0.25 0.007 70));
17
+ --color-surface: light-dark(oklch(0.987 0.005 70), oklch(0.265 0.007 70));
18
+ --color-surface-alt: var(--color-surface);
19
+ /* Menus, popovers and tooltips. */
20
+ --color-raised: light-dark(oklch(1 0 70), oklch(0.295 0.007 70));
21
+
22
+ /* States, washes of the brightest text so they work on any surface. `on` is a toggle that is on. */
23
+ --color-hover: light-dark(
24
+ color-mix(in oklab, var(--color-text-0) 5.5%, transparent),
25
+ color-mix(in oklab, var(--color-text-0) 7.5%, transparent)
26
+ );
27
+ --color-press: light-dark(
28
+ color-mix(in oklab, var(--color-text-0) 9.5%, transparent),
29
+ color-mix(in oklab, var(--color-text-0) 13%, transparent)
30
+ );
31
+ --color-on: light-dark(
32
+ color-mix(in oklab, var(--color-accent) 13%, transparent),
33
+ color-mix(in oklab, var(--color-accent) 16%, transparent)
34
+ );
35
+ --color-on-hover: light-dark(
36
+ color-mix(in oklab, var(--color-accent) 22%, transparent),
37
+ color-mix(in oklab, var(--color-accent) 27%, transparent)
38
+ );
39
+
40
+ /* Separators. */
41
+ --color-border: light-dark(
42
+ color-mix(in oklab, var(--color-text-0) 8%, transparent),
43
+ color-mix(in oklab, var(--color-text-0) 7.5%, transparent)
44
+ );
45
+ --color-hairline: var(--color-border);
46
+ --color-border-strong: light-dark(
47
+ color-mix(in oklab, var(--color-text-0) 15%, transparent),
48
+ color-mix(in oklab, var(--color-text-0) 14%, transparent)
49
+ );
50
+ --color-border-input: var(--color-border-strong);
51
+
52
+ /* Text: primary, secondary, muted (4.5:1), and off (3:1, disabled controls only). */
53
+ --color-text-0: light-dark(oklch(0.205 0.003 70), oklch(0.965 0.004 70));
54
+ --color-text-1: var(--color-text-0);
55
+ --color-text-body: var(--color-text-0);
56
+ --color-text-2: light-dark(oklch(0.403 0.003 70), oklch(0.908 0.004 70));
57
+ --color-text-3: var(--color-text-2);
58
+ --color-text-muted: light-dark(oklch(0.503 0.003 70), oklch(0.777 0.004 70));
59
+ --color-text-4: var(--color-text-muted);
60
+ --color-text-5: var(--color-text-muted);
61
+ --color-text-off: light-dark(oklch(0.597 0.003 70), oklch(0.67 0.004 70));
34
62
 
35
63
  /* Roles. Selection and playhead are reserved and differ from the accent so a
36
- * selected clip, the playhead and an accented control stay tellable apart. */
37
- --color-accent: #3ce6ac;
38
- --color-selection: #4ba3d2;
39
- --color-playhead: #ffffff;
40
- --color-danger: #ef4444;
41
- --color-media: #00e3ff;
42
- --color-container: #f5a623;
64
+ * selected clip, the playhead and an accented control stay tellable apart.
65
+ * `accent-ink` is accent text on a surface; `on-accent` is text on an accent fill. */
66
+ --color-accent: light-dark(oklch(0.536 0.114 164), oklch(0.82 0.159 164));
67
+ --color-accent-hover: light-dark(oklch(0.486 0.103 164), oklch(0.87 0.159 164));
68
+ --color-on-accent: light-dark(oklch(1 0 0), oklch(0.2 0.043 164));
69
+ --color-accent-ink: light-dark(oklch(0.45 0.096 164), oklch(0.827 0.159 164));
70
+ --color-ring: var(--color-accent-ink);
71
+ --color-selection: light-dark(oklch(0.5 0.13 240), #4ba3d2);
72
+ --color-playhead: var(--color-text-0);
73
+ --color-danger: oklch(0.56 0.19 25);
74
+ --color-danger-ink: light-dark(oklch(0.45 0.17 25), oklch(0.791 0.12 25));
75
+ --color-warning-ink: light-dark(oklch(0.5 0.12 70), oklch(82.8% 0.189 84.429));
76
+ --color-on-danger: oklch(1 0 0);
77
+ --color-media: light-dark(oklch(0.5 0.1 215), #00e3ff);
78
+ --color-container: light-dark(oklch(0.55 0.13 70), #f5a623);
79
+
80
+ /* Code editor syntax: One Light and One Dark hues, held at 4.5:1 on a line, the active line and a selection on it. */
81
+ --color-code-keyword: light-dark(oklch(0.509 0.211 329), oklch(0.829 0.164 318));
82
+ --color-code-name: light-dark(oklch(0.504 0.179 28), oklch(0.856 0.145 17));
83
+ --color-code-function: light-dark(oklch(0.492 0.193 263), oklch(0.805 0.121 245));
84
+ --color-code-constant: light-dark(oklch(0.495 0.13 50), oklch(0.815 0.11 50));
85
+ --color-code-type: light-dark(oklch(0.48 0.12 92), oklch(0.805 0.1 92));
86
+ --color-code-operator: light-dark(oklch(0.471 0.127 237), oklch(0.793 0.092 206));
87
+ --color-code-comment: light-dark(oklch(0.48 0.009 279), oklch(0.806 0.03 262));
88
+ --color-code-string: light-dark(oklch(0.468 0.142 143), oklch(0.793 0.11 133));
43
89
 
44
90
  /* The two stops of the HeyGen brand gradient, worn by the header logo. Fixed brand values
45
91
  * rather than roles, so they are not reachable as a background or text colour by mistake. */
46
92
  --color-brand-gradient-from: #06e3fa;
47
93
  --color-brand-gradient-to: #4fdb5e;
48
94
 
49
- /* The timeline's own surfaces: darker and bluer than the shared scale, so they
50
- * get their own names instead of snapping to the nearest --color-bg/-surface
51
- * step (a host themes the timeline by overriding these). */
52
- --timeline-shell-bg: #0a0a0b;
53
- --timeline-shell-border: rgba(255, 255, 255, 0.05);
54
- --timeline-ruler-border: rgba(255, 255, 255, 0.16);
55
- --timeline-row-bg: #101014;
56
- --timeline-row-border: rgba(255, 255, 255, 0.06);
57
- --timeline-gutter-bg: #0e0f12;
58
- --timeline-gutter-border: rgba(255, 255, 255, 0.1);
59
- --timeline-text-primary: rgba(255, 255, 255, 0.92);
60
- --timeline-text-secondary: rgba(255, 255, 255, 0.62);
61
- --timeline-tick-text: rgba(255, 255, 255, 0.34);
62
- --timeline-tick-major: rgba(255, 255, 255, 0.1);
63
- --timeline-tick-minor: rgba(255, 255, 255, 0.06);
64
- --timeline-clip-bg: rgba(255, 255, 255, 0.12);
65
- --timeline-clip-label-active: rgba(232, 255, 247, 0.95);
95
+ /* The timeline's own surfaces and inks, named so a host can theme the timeline alone. */
96
+ --timeline-shell-bg: var(--color-surface);
97
+ --timeline-shell-border: var(--color-border);
98
+ --timeline-ruler-border: color-mix(in oklab, var(--color-text-0) 16%, transparent);
99
+ --timeline-row-bg: light-dark(oklch(0.997 0.002 70), oklch(0.275 0.007 70));
100
+ --timeline-row-border: color-mix(in oklab, var(--color-text-0) 6%, transparent);
101
+ --timeline-gutter-bg: light-dark(oklch(0.967 0.005 70), oklch(0.253 0.007 70));
102
+ --timeline-gutter-border: color-mix(in oklab, var(--color-text-0) 10%, transparent);
103
+ --timeline-text-primary: var(--color-text-0);
104
+ --timeline-text-secondary: var(--color-text-2);
105
+ --timeline-tick-text: var(--color-text-2);
106
+ --timeline-tick-major: color-mix(in oklab, var(--color-text-0) 10%, transparent);
107
+ --timeline-tick-minor: color-mix(in oklab, var(--color-text-0) 6%, transparent);
108
+ --timeline-clip-bg: light-dark(
109
+ color-mix(in oklab, var(--color-text-0) 12%, transparent),
110
+ color-mix(in oklab, var(--color-text-0) 13%, transparent)
111
+ );
112
+ --timeline-clip-label-active: light-dark(#0b3d2c, rgba(232, 255, 247, 0.95));
66
113
  --timeline-clip-label-shadow: 0 1px 2px rgba(0, 0, 0, 0.85);
67
- --timeline-clip-audio-bg: rgba(167, 139, 250, 0.16);
68
- --timeline-clip-audio-border: rgba(167, 139, 250, 0.4);
69
- --timeline-clip-audio-bg-hover: rgba(167, 139, 250, 0.24);
70
- --timeline-clip-audio-bg-dragging: rgba(60, 52, 84, 0.96);
114
+ /* Audio clips are a solid fill that carries white ink in both themes. */
115
+ --timeline-clip-audio-bg: oklch(0.45 0.12 300);
116
+ --timeline-clip-audio-border: oklch(0.37 0.12 300);
117
+ --timeline-clip-audio-bg-hover: oklch(0.49 0.12 300);
118
+ --timeline-clip-audio-bg-dragging: oklch(0.42 0.12 300 / 0.96);
71
119
  --timeline-clip-audio-muted-bg: rgb(72, 74, 78);
72
- --timeline-audio-link: #a0aaa4;
73
- --timeline-clip-selection: rgba(255, 255, 255, 0.85);
120
+ --timeline-audio-link: light-dark(#626d66, #a0aaa4);
121
+ --timeline-clip-selection: light-dark(#0f766e, rgba(255, 255, 255, 0.85));
74
122
  --timeline-clip-selection-highlight: rgb(255 255 255 / 0.55);
75
123
  --timeline-clip-radius: 8px;
76
124
  --timeline-clip-audio-radius: 21px;
@@ -79,7 +127,7 @@
79
127
  --timeline-clip-chip-bg: rgba(15, 22, 18, 0.73);
80
128
  --timeline-clip-chip-audio-bg: rgba(25, 16, 38, 0.7);
81
129
  --timeline-clip-chip-text: #ffffff;
82
- --timeline-shortcut-bg: rgba(17, 23, 35, 0.84);
130
+ --timeline-shortcut-bg: color-mix(in oklab, var(--color-raised) 94%, transparent);
83
131
  /* Composition and text clips draw their frame as it is, like video filmstrips; a host may dim them. */
84
132
  --timeline-composition-thumbnail-opacity: 1;
85
133
  --timeline-composition-thumbnail-blend: normal;
@@ -105,58 +153,82 @@
105
153
  transparent 2px 5px
106
154
  );
107
155
  --timeline-waveform-label-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
108
- --timeline-clip-bg-active: rgba(60, 230, 172, 0.2);
109
- --timeline-clip-bg-hover: rgba(255, 255, 255, 0.16);
110
- --timeline-clip-bg-dragging: rgba(38, 42, 52, 0.96);
111
- --timeline-clip-border: rgba(255, 255, 255, 0.22);
112
- --timeline-clip-border-hover: rgba(60, 230, 172, 0.75);
113
- --timeline-clip-border-active: rgba(60, 230, 172, 0.55);
114
- --timeline-handle: rgba(255, 255, 255, 0.55);
156
+ --timeline-clip-bg-active: color-mix(in oklab, var(--color-accent) 20%, transparent);
157
+ --timeline-clip-bg-hover: light-dark(
158
+ color-mix(in oklab, var(--color-text-0) 26%, transparent),
159
+ color-mix(in oklab, var(--color-text-0) 16%, transparent)
160
+ );
161
+ --timeline-clip-bg-dragging: color-mix(in oklab, var(--color-raised) 96%, transparent);
162
+ --timeline-clip-border: light-dark(
163
+ color-mix(in oklab, var(--color-text-0) 40%, transparent),
164
+ color-mix(in oklab, var(--color-text-0) 30%, transparent)
165
+ );
166
+ --timeline-clip-border-hover: color-mix(in oklab, var(--color-accent) 75%, transparent);
167
+ --timeline-clip-border-active: light-dark(
168
+ color-mix(in oklab, var(--color-accent) 90%, transparent),
169
+ color-mix(in oklab, var(--color-accent) 55%, transparent)
170
+ );
171
+ --timeline-handle: var(--color-text-0);
115
172
  /* Audio clip fades: the shaded wedge over the faded gain, and the corner dots. */
116
173
  --timeline-fade-shade: rgba(0, 0, 0, 0.38);
117
174
  --timeline-fade-dot: #fff;
118
175
  --timeline-fade-dot-ring: rgba(0, 0, 0, 0.55);
119
- --timeline-resize-seam: rgba(255, 255, 255, 0.12);
120
- --timeline-resize-active: rgba(255, 255, 255, 0.24);
121
- --timeline-track-clip-fill: rgba(255, 255, 255, 0.055);
122
- --timeline-track-clip-active: rgba(60, 230, 172, 0.16);
123
- --timeline-track-label: rgba(255, 255, 255, 0.5);
124
- --timeline-border-strong: rgba(255, 255, 255, 0.2);
125
- --timeline-group-member-tint: rgba(255, 255, 255, 0.035);
126
- --timeline-overlay-text: rgba(255, 255, 255, 0.8);
127
- --timeline-accent: #3ce6ac;
128
- --timeline-accent-soft: rgba(60, 230, 172, 0.18);
129
- --timeline-accent-faint: rgba(60, 230, 172, 0.055);
130
- --timeline-accent-fill: rgba(60, 230, 172, 0.1);
131
- --timeline-accent-glow: rgba(60, 230, 172, 0.5);
132
- --timeline-accent-border: rgba(60, 230, 172, 0.7);
133
- --timeline-accent-rail: rgba(60, 230, 172, 0.35);
134
- --timeline-accent-hover: rgba(60, 230, 172, 0.25);
135
- --timeline-accent-button-bg: rgba(60, 230, 172, 0.15);
136
- --timeline-danger: rgba(239, 68, 68, 0.7);
176
+ --timeline-resize-seam: color-mix(in oklab, var(--color-text-0) 12%, transparent);
177
+ --timeline-resize-active: color-mix(in oklab, var(--color-text-0) 24%, transparent);
178
+ --timeline-track-clip-fill: color-mix(in oklab, var(--color-text-0) 8%, transparent);
179
+ --timeline-track-clip-active: color-mix(in oklab, var(--color-accent) 20%, transparent);
180
+ --timeline-track-label: var(--color-text-muted);
181
+ --timeline-border-strong: color-mix(in oklab, var(--color-text-0) 20%, transparent);
182
+ --timeline-group-member-tint: color-mix(in oklab, var(--color-text-0) 3.5%, transparent);
183
+ --timeline-overlay-text: var(--color-text-2);
184
+ --timeline-accent: var(--color-accent);
185
+ --timeline-accent-soft: color-mix(in oklab, var(--color-accent) 18%, transparent);
186
+ --timeline-accent-faint: color-mix(in oklab, var(--color-accent) 5.5%, transparent);
187
+ --timeline-accent-fill: color-mix(in oklab, var(--color-accent) 10%, transparent);
188
+ --timeline-accent-glow: color-mix(in oklab, var(--color-accent) 50%, transparent);
189
+ --timeline-accent-border: color-mix(in oklab, var(--color-accent) 70%, transparent);
190
+ --timeline-accent-rail: color-mix(in oklab, var(--color-accent) 35%, transparent);
191
+ --timeline-accent-hover: var(--color-on-hover);
192
+ --timeline-accent-button-bg: var(--color-on);
193
+ --timeline-danger: color-mix(in oklab, var(--color-danger) 70%, transparent);
137
194
  --timeline-info-bg: rgba(59, 130, 246, 0.12);
138
195
  --timeline-info-border: rgba(59, 130, 246, 0.4);
139
- --timeline-dialog-bg: #1b1b1f;
140
- --timeline-diamond-bg: #171717;
141
- --timeline-diamond-border: rgba(255, 255, 255, 0.14);
142
- --timeline-text-solid: #ffffff;
143
- --timeline-text-faint: rgba(255, 255, 255, 0.35);
144
- --timeline-text-dim: rgba(255, 255, 255, 0.4);
145
- --timeline-text-soft: rgba(255, 255, 255, 0.75);
146
- --timeline-text-bright: rgba(255, 255, 255, 0.7);
147
- --timeline-snap-guide: rgba(255, 255, 255, 0.6);
148
- --timeline-overlay-bg: rgba(0, 0, 0, 0.8);
149
- --timeline-overlay-bg-strong: rgba(0, 0, 0, 0.85);
150
- --timeline-input-bg: rgba(0, 0, 0, 0.3);
151
- --timeline-automation-rail: rgba(255, 255, 255, 0.08);
152
- --timeline-automation-point-muted: rgba(0, 0, 0, 0.5);
153
- --timeline-diamond-muted: #a3a3a3;
196
+ --timeline-dialog-bg: var(--color-raised);
197
+ --timeline-diamond-bg: var(--color-raised);
198
+ --timeline-diamond-border: color-mix(in oklab, var(--color-text-0) 14%, transparent);
199
+ --timeline-text-solid: var(--color-text-0);
200
+ --timeline-text-faint: var(--color-text-off);
201
+ --timeline-text-dim: var(--color-text-muted);
202
+ --timeline-text-soft: var(--color-text-2);
203
+ --timeline-text-bright: var(--color-text-2);
204
+ --timeline-snap-guide: color-mix(in oklab, var(--color-text-0) 60%, transparent);
205
+ --timeline-overlay-bg: light-dark(oklch(1 0 70 / 0.9), rgba(0, 0, 0, 0.8));
206
+ --timeline-overlay-bg-strong: light-dark(oklch(1 0 70 / 0.95), rgba(0, 0, 0, 0.85));
207
+ --timeline-input-bg: light-dark(
208
+ color-mix(in oklab, var(--color-text-0) 5%, transparent),
209
+ rgba(0, 0, 0, 0.3)
210
+ );
211
+ --timeline-automation-rail: color-mix(in oklab, var(--color-text-0) 8%, transparent);
212
+ --timeline-automation-point-muted: light-dark(
213
+ color-mix(in oklab, var(--color-text-0) 30%, transparent),
214
+ rgba(0, 0, 0, 0.5)
215
+ );
216
+ --timeline-diamond-muted: var(--color-text-muted);
154
217
  --timeline-beat: rgb(34, 197, 94);
155
- --timeline-clip-shadow-dragging: rgba(0, 0, 0, 0.4);
156
- --timeline-playhead-glow: rgba(60, 230, 172, 0.14);
157
- --timeline-playhead-shadow: rgba(0, 0, 0, 0.55);
158
- --timeline-transition-zone: rgba(255, 255, 255, 0.2);
159
- --timeline-transition-badge: rgba(237, 244, 239, 0.98);
218
+ --timeline-clip-shadow-dragging: light-dark(
219
+ color-mix(in oklab, var(--color-text-0) 16%, transparent),
220
+ rgba(0, 0, 0, 0.4)
221
+ );
222
+ --timeline-playhead-glow: color-mix(in oklab, var(--color-accent) 14%, transparent);
223
+ --timeline-playhead-shadow: light-dark(
224
+ color-mix(in oklab, var(--color-text-0) 20%, transparent),
225
+ rgba(0, 0, 0, 0.55)
226
+ );
227
+ --timeline-transition-zone: light-dark(
228
+ color-mix(in oklab, var(--color-text-0) 12%, transparent),
229
+ color-mix(in oklab, var(--color-text-0) 20%, transparent)
230
+ );
231
+ --timeline-transition-badge: light-dark(#ffffff, rgba(237, 244, 239, 0.98));
160
232
  --timeline-transition-badge-ink: rgba(36, 56, 45, 0.96);
161
233
 
162
234
  /* -------------------------------------------------------------- geometry */
@@ -197,14 +269,16 @@
197
269
 
198
270
  /* ---------------------------------------------------------------- shadow */
199
271
 
200
- /* Floating chrome over a near-black canvas: the hairline ring does the edge
201
- * definition that a light-mode drop shadow cannot. */
202
- --shadow-menu: 0 8px 24px rgb(0 0 0 / 0.45), 0 0 0 1px rgb(0 0 0 / 0.4);
203
- --shadow-popover: 0 16px 40px rgb(0 0 0 / 0.55), 0 0 0 1px rgb(0 0 0 / 0.45);
272
+ /* Floating chrome: a drop shadow plus a hairline ring that defines the edge. */
273
+ --shadow-menu:
274
+ 0 8px 24px light-dark(rgb(0 0 0 / 0.12), rgb(0 0 0 / 0.45)),
275
+ 0 0 0 1px light-dark(rgb(0 0 0 / 0.07), rgb(0 0 0 / 0.4));
276
+ --shadow-popover:
277
+ 0 16px 40px light-dark(rgb(0 0 0 / 0.14), rgb(0 0 0 / 0.55)),
278
+ 0 0 0 1px light-dark(rgb(0 0 0 / 0.07), rgb(0 0 0 / 0.45));
204
279
 
205
- /* The primary button is the one light surface in a dark UI, so it reads as a
206
- * raised key rather than a floating panel: a tight contact shadow, a soft
207
- * ambient one, and an inner top highlight. */
280
+ /* The primary button reads as a raised key rather than a floating panel: a tight
281
+ * contact shadow, a soft ambient one, and an inner top highlight. */
208
282
  --shadow-btn-primary:
209
283
  0 1px 2px rgb(0 0 0 / 0.6), 0 2px 6px rgb(0 0 0 / 0.35), inset 0 1px 0 rgb(255 255 255 / 0.65);
210
284
 
@@ -277,9 +351,9 @@
277
351
  * strip sits below the card body and the shown tab is a fill in the body colour. */
278
352
  --panel-shell-bg: var(--color-bg-1);
279
353
  --panel-card-bg: var(--color-surface);
280
- --panel-card-border: #303034;
354
+ --panel-card-border: var(--color-border-strong);
281
355
  --panel-strip-bg: var(--color-bg-0);
282
- --panel-tab-bg-hover: #202023;
356
+ --panel-tab-bg-hover: var(--color-hover);
283
357
 
284
358
  /* Names Studio's markup invented that no config ever defined, so the classes
285
359
  * compiled to nothing. Aliased to the token that was meant, rather than
@@ -291,9 +365,9 @@
291
365
 
292
366
  /* ------------------------------------------- deprecated: Tailwind palette */
293
367
 
294
- /* Verbatim Tailwind 4.3.3 defaults for stock colours markup still names, so
295
- * `--color-*: initial` does not blank them. U12 deletes this block;
296
- * `theme.test.ts` fails if a value drifts from upstream. */
368
+ /* Stock colours markup still names, so `--color-*: initial` does not blank them. U12 deletes
369
+ * this block. The neutral scale is Graphite's roles by the job each step does in markup (light
370
+ * steps text, dark steps surfaces and lines); the rest are verbatim Tailwind 4.3.3 defaults. */
297
371
  --color-amber-50: oklch(98.7% 0.022 95.277);
298
372
  --color-amber-100: oklch(96.2% 0.059 95.617);
299
373
  --color-amber-200: oklch(92.4% 0.12 95.746);
@@ -315,17 +389,17 @@
315
389
  --color-green-400: oklch(79.2% 0.209 151.711);
316
390
  --color-green-500: oklch(72.3% 0.219 149.579);
317
391
  --color-green-600: oklch(62.7% 0.194 149.214);
318
- --color-neutral-50: oklch(98.5% 0 none);
319
- --color-neutral-100: oklch(97% 0 none);
320
- --color-neutral-200: oklch(92.2% 0 none);
321
- --color-neutral-300: oklch(87% 0 none);
322
- --color-neutral-400: oklch(70.8% 0 none);
323
- --color-neutral-500: oklch(55.6% 0 none);
324
- --color-neutral-600: oklch(43.9% 0 none);
325
- --color-neutral-700: oklch(37.1% 0 none);
326
- --color-neutral-800: oklch(26.9% 0 none);
327
- --color-neutral-900: oklch(20.5% 0 none);
328
- --color-neutral-950: oklch(14.5% 0 none);
392
+ --color-neutral-50: var(--color-text-0);
393
+ --color-neutral-100: var(--color-text-0);
394
+ --color-neutral-200: var(--color-text-0);
395
+ --color-neutral-300: var(--color-text-2);
396
+ --color-neutral-400: var(--color-text-2);
397
+ --color-neutral-500: var(--color-text-muted);
398
+ --color-neutral-600: var(--color-text-muted);
399
+ --color-neutral-700: var(--color-border-strong);
400
+ --color-neutral-800: var(--color-border);
401
+ --color-neutral-900: var(--color-surface);
402
+ --color-neutral-950: var(--color-bg-0);
329
403
  --color-orange-300: oklch(83.7% 0.128 66.29);
330
404
  --color-orange-400: oklch(75% 0.183 55.934);
331
405
  --color-pink-400: oklch(71.8% 0.202 349.761);
@@ -359,31 +433,24 @@
359
433
  --color-white: #fff;
360
434
  }
361
435
 
362
- /* One paper block. A host sets data-theme="paper" on the document element. */
436
+ /* The colour-scheme picks each token's light or dark half: dark unless the document element
437
+ * has data-theme="paper". Base layer, so a host's own colour-scheme wins. */
438
+ @layer base {
439
+ :root {
440
+ color-scheme: dark;
441
+ }
442
+ :root:where([data-theme="paper"]) {
443
+ color-scheme: light;
444
+ }
445
+ }
363
446
  :root[data-theme="paper"] {
364
- --timeline-shell-bg: #f4f6f3;
365
- --timeline-row-bg: #edf0ed;
366
- --timeline-clip-bg: #c9d1cb;
447
+ --timeline-clip-label-shadow: none;
367
448
  --timeline-clip-shadow: 0 2px 6px rgb(0 0 0 / 0.12);
368
- --timeline-clip-highlight: rgb(255 255 255 / 0.08);
369
- --timeline-clip-selection: #0f766e;
370
- --timeline-clip-selection-highlight: rgb(255 255 255 / 0.55);
371
- --timeline-clip-chip-bg: rgba(15, 22, 18, 0.73);
372
- --timeline-clip-chip-audio-bg: rgba(25, 16, 38, 0.7);
373
- --timeline-clip-chip-text: #ffffff;
374
- --timeline-clip-audio-bg: rgb(150, 128, 180);
375
- --timeline-clip-audio-bg-hover: rgb(135, 109, 170);
376
- --timeline-clip-audio-bg-dragging: rgb(232, 224, 242);
377
- --timeline-clip-audio-border: rgba(113, 80, 155, 0.45);
378
- --timeline-clip-audio-muted-bg: rgb(156, 161, 157);
379
- --timeline-audio-link: #626d66;
380
- --timeline-waveform-bar-rgb: 20, 12, 32;
381
- --timeline-waveform-muted-rgb: 24, 28, 26;
382
- --timeline-waveform-baseline-rgb: 76, 62, 88;
383
- --timeline-waveform-error: repeating-linear-gradient(
449
+ --timeline-thumbnail-shimmer: linear-gradient(
384
450
  90deg,
385
- rgba(20, 12, 32, 0.35) 0 2px,
386
- transparent 2px 5px
451
+ rgba(24, 24, 27, 0.02) 0%,
452
+ rgba(24, 24, 27, 0.05) 50%,
453
+ rgba(24, 24, 27, 0.02) 100%
387
454
  );
388
455
  }
389
456
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  /** Guards the ways `theme.css` can silently break: a dropped CSS custom
4
4
  * property, a utility that stops compiling, the stock Tailwind palette
5
- * leaking back in, or the JS preset / legacy colours drifting from it. */
5
+ * leaking back in, or the legacy colours drifting from it. */
6
6
 
7
7
  import { readFileSync } from "node:fs";
8
8
  import path from "node:path";
@@ -11,7 +11,6 @@ import { createRoot } from "react-dom/client";
11
11
  import { Gear, IconContext } from "@phosphor-icons/react";
12
12
  import { compile } from "tailwindcss";
13
13
  import { afterEach, describe, expect, it } from "vitest";
14
- import studioPreset from "./tailwind-preset.shared.js";
15
14
  import { readIconTokens } from "./iconTokens";
16
15
  import { loadStylesheet, STYLES_DIR, TAILWIND_DIR } from "./styleSources";
17
16
 
@@ -52,7 +51,9 @@ describe("studio theme", () => {
52
51
  it("emits the semantic palette as custom properties and as utilities", async () => {
53
52
  const css = await build("studio.css", ["bg-accent", "text-text-2", "border-border"]);
54
53
 
55
- expect(rootVariables(css).get("--color-accent")).toBe("#3ce6ac");
54
+ expect(rootVariables(css).get("--color-accent")).toBe(
55
+ "light-dark(oklch(0.536 0.114 164), oklch(0.82 0.159 164))",
56
+ );
56
57
  expect(css).toContain(".bg-accent {");
57
58
  expect(css).toContain("background-color: var(--color-accent)");
58
59
  });
@@ -127,7 +128,7 @@ describe("studio theme", () => {
127
128
  expect(css).toContain(".duration-open {");
128
129
  });
129
130
 
130
- it("keeps every legacy Tailwind palette entry at its upstream default value", async () => {
131
+ it("keeps every legacy Tailwind palette entry that is not a role at its upstream value", async () => {
131
132
  // These are copies of Tailwind's own values, kept only so existing markup
132
133
  // renders unchanged. A Tailwind upgrade that moves one of them would
133
134
  // otherwise silently change Studio's colors.
@@ -138,7 +139,9 @@ describe("studio theme", () => {
138
139
  ),
139
140
  ].map(([, name, value]) => [name, value.trim()]),
140
141
  );
141
- const legacy = [...themeSource.matchAll(/\n\s*(--color-[a-z]+-(?:50|\d00|950)):\s*([^;]+);/g)];
142
+ const legacy = [
143
+ ...themeSource.matchAll(/\n\s*(--color-[a-z]+-(?:50|\d00|950)):\s*([^;]+);/g),
144
+ ].filter(([, , value]) => !value.trim().startsWith("var("));
142
145
 
143
146
  expect(legacy.length).toBeGreaterThan(0);
144
147
  for (const [, name, value] of legacy) {
@@ -147,11 +150,12 @@ describe("studio theme", () => {
147
150
  }
148
151
  });
149
152
 
150
- it("keeps the deprecated JS preset in step with the CSS it shadows", () => {
151
- const colors = studioPreset.theme.extend.colors;
153
+ it("maps every neutral step onto a Graphite role, so the stock scale follows the theme", () => {
154
+ const steps = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
152
155
 
153
- expect(colors.studio.accent.toLowerCase()).toBe(declaredValue("--color-accent"));
154
- expect(colors.panel.surface.toLowerCase()).toBe(declaredValue("--color-surface"));
156
+ for (const step of steps) {
157
+ expect(declaredValue(`--color-neutral-${step}`)).toMatch(/^var\(--color-[a-z0-9-]+\)$/);
158
+ }
155
159
  });
156
160
 
157
161
  it("reads the icon defaults off the theme rather than hard-coding them", () => {