close
Skip to content

feat: New color formats and color picker#5661

Merged
kof merged 38 commits intomainfrom
color-formats
Mar 27, 2026
Merged

feat: New color formats and color picker#5661
kof merged 38 commits intomainfrom
color-formats

Conversation

@kof
Copy link
Copy Markdown
Member

@kof kof commented Mar 25, 2026

kof added 30 commits March 24, 2026 15:40
colorjs internal names (p3, a98rgb, prophoto) differ from the CSS
predefined color space identifiers required by the spec:
  p3       → display-p3
  a98rgb   → a98-rgb
  prophoto → prophoto-rgb

Without this, toValue() emitted e.g. color(p3 ...) which the css-tree
lexer rejects as invalid <color>, causing the style panel's CSS value
input to fail on round-trip validation for these color spaces.
- css-data: add color-mix( short-circuit to isValidDeclaration so
  color-mix() values pass through as UnparsedValue in non-browser
  environments where csstree lexer is used as fallback
- design-system: add resolveColorViaCanvas fallback in parseColorString
  so color-mix() and any other CSS Color 5 values the browser resolves
  but colorjs doesn't show a correct swatch instead of transparent black
- builder color-picker: add UnparsedValue to both onChange and
  onChangeComplete allowlists so color-mix() typed in the CSS value
  input is saved and not treated as invalid
- tests: add 5 tests for color-mix() parsing including all var() positions
Relative color syntax (CSS Color 5): rgb(from <origin> r g b / alpha),
oklch(from <origin> l c h), hsl(from <origin> h s l), etc. is now
accepted as a valid UnparsedValue in all color properties.

- css-data: add /\(\s*from\s/ guard to isValidDeclaration so static
  relative colors (no var()) pass through in non-browser environments
  where csstree lexer doesn't know the 'from' keyword inside color
  functions. Values with var() already bypassed via the existing guard.
- swatch display already works via the canvas fallback added for
  color-mix (resolveColorViaCanvas); after substituteVars resolves
  any var() refs the browser can render rgb(from #hex r g b / 25%)
- css value input already works via the UnparsedValue allowlist fix
  from the color-mix commit
- tests: 7 tests covering all var() positions (origin, channel, alpha,
  origin+alpha) plus static relative colors and multiple color functions
  (rgb, hsl, oklch) and multiple properties
…sed detection

- Remove value.includes("var(") string check; detect var() nodes structurally
  via a single csstree walk at the top of isValidDeclaration
- Remove value.includes("color-mix(") heuristic entirely — csstree lexer already
  handles color-mix() correctly (probe confirmed matched:true)
- Replace /\(\s*from\s/ regex and then value.includes("(from ") with a structural
  check: any Function node whose first child is Identifier 'from' is relative color
- Parse the value once upfront and reuse the AST for both the walk and the
  subsequent lexer.matchProperty call — eliminates a second cssTryParseValue call
- Move the CSSStyleValue.parse browser branch before the csstree lexer path so
  the browser always uses the authoritative native validator
- Add isValidDeclaration direct tests covering all branches:
  custom props, var() on any property, var() on keyword-only properties (ordering
  regression guard), keyword-only valid/invalid, static/var relative color,
  var() nested in calc()/color-mix(), unparseable input (ast==null), linear()
  easing on transition/animation-timing-function, unknown property passthrough
- Add "hex" to ColorValue.colorSpace union in schema.ts
- Serialize hex colorSpace to #rrggbb / #rrggbbaa in to-value.ts
- Parse CSS hash tokens as ColorValue { colorSpace: "hex" } in parse-css-value.ts
- Fix parseColor() to normalize css-tree's minified negative numbers (0.1-0.1 → 0.1 -0.1) before passing to colorjs
- Use AbortController for event listeners in color-picker.tsx
- Export cssStringToStyleValue for testability
- Add comprehensive color space tests for all spaces in:
  - packages/css-engine/src/core/to-value.test.ts
  - packages/css-data/src/parse-css-value.test.ts
  - apps/builder/.../parse-intermediate-or-invalid-value.ts.test.ts
  - packages/design-system/src/components/color-picker.test.ts
…nterdown preventDefault

- Store onChange/onChangeComplete/onOpenChange/disableCanvasPointerEvents/
  enableCanvasPointerEvents in a ref so the event-wiring effect runs only
  once (on mount) without stale-closure issues
- Add pointerdown preventDefault to prevent focus stealing
- Inline skipInertHandlersAttribute constant to remove cross-package import
  from builder app into design-system
…ed statically in jsdom/node test environments
…e for server tests, drop all polyfills except CSS.supports
…nAllTimers

Replace 9x  with
synchronous  after adding  to beforeEach.

Also flush pending fake timers in afterEach (before vi.useRealTimers()) to
reset the raf-queue module's handle state between tests. Without this,
tests that don't flush their own rAF leave handle !== undefined, causing
subsequent tests to skip scheduling a new rAF in batchUpdate.
kof added 8 commits March 26, 2026 16:50
- Move colorjs setup and color utilities to packages/css-engine/src/color.ts
- Replace RgbaColor with PlainColorObject from colorjs.io
- Export colorjs namespace as 'color' with all spaces pre-registered
- Add toColorSpace() with exhaustive Map<ColorSpace, ColorValue['colorSpace']>
- Register CSS aliases (display-p3, a98-rgb, prophoto-rgb) in ColorSpace registry
- Add color helpers: srgbColor, parseColor, colorDistance, lerpColor, serializeColor
- Remove colorjs.io dependency from css-data and design-system packages
- Add comprehensive tests for all color utilities
@kof kof merged commit 9e72cc2 into main Mar 27, 2026
20 of 22 checks passed
@kof kof deleted the color-formats branch March 27, 2026 00:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant