## Summary
Rebuilds Webstudio’s color system, migrates the entire Builder to it,
and introduces a complete dark theme.
A small set of theme colors and contrast controls now drives the
interface through relative CSS colors:
```text
Theme colors and contrast
↓
Light/dark derivation bounds
↓
Derived color families
↓
Reusable semantic colors
↓
Component composition
↓
Builder interface
```
CSS is the runtime source of truth. Light and dark modes share the same
theme inputs, so changing a theme color automatically updates its
surfaces, content, borders, states, and component presentation. The
refactor turns dark mode into a first-class Builder theme rather than a
separately maintained palette.
This is an atomic migration. Legacy color names and compatibility
aliases will not remain after all consumers have moved.
## Scope
- The color source, derivation model, semantic vocabulary, validation,
typing, and Storybook overview.
- Every design-system component.
- Builder, dashboard, authentication, shared UI, Query Builder, stories,
and tests that consume the legacy color scale.
- Removal of the legacy Stitches color scale after its final consumer is
migrated.
- A complete dark theme across application UI while keeping authored
canvas content independent.
- Complete light and dark visual verification.
- Unified browser-test infrastructure: Vitest Browser Mode for Design
System browser contracts and standard Playwright Test for all Builder
E2E workflows.
Literal colors remain only where color is the actual content or belongs
to a deliberately separate system, such as user-authored values,
external previews, brand artwork, data visualization, functional canvas
overlays, and source assets. Every remaining exception must have an
explicit owner.
## Architecture decisions
- Theme authors control six color families and three bounded contrast
controls.
- Relative CSS colors derive the rest of the system.
- Dark mode changes derivation bounds instead of maintaining a second
independent palette.
- Components consume a small reusable semantic vocabulary.
- Component states and exceptional presentation are composed inside
components.
- Builder-specific categorical meaning uses focused composite recipes,
not intent semantics or a larger global vocabulary.
- TypeScript validates public semantic CSS-variable names but does not
generate runtime colors.
- No token-manager dependency, compatibility aliases, or
component-specific public color tokens.
- A new semantic or theme variable is added only when existing semantics
and local composition cannot represent a reusable cross-feature
decision.
## Todo
### Color-system foundation
- [x] Establish direct CSS as the runtime source of truth.
- [x] Define bounded theme colors and contrast controls.
- [x] Derive light and dark modes from the same theme inputs.
- [x] Reduce the semantic inventory to reusable decisions and explicit
accessibility pairs.
- [x] Generate only TypeScript names for public semantic CSS variables.
- [x] Validate layers, references, cycles, consumption, naming,
generated types, and browser-computed contrast.
- [x] Add the Storybook color-system overview and theme controls.
- [x] Document the architecture and its relationship to the public Craft
specification.
### Design-system migration
- [x] Replace every active legacy color consumer in design-system
components and Storybook chrome.
- [x] Compose component states locally without component-specific public
tokens.
- [x] Preserve intentional preview and sample-content colors.
- [x] Add complete positive, negative, warning, informative, and accent
accessibility pairings.
- [x] Validate light and dark scheme-bound references independently.
- [x] Verify the design-system package with typecheck, lint, formatting,
Storybook, and 222 tests.
### Inventory and contract
- [x] Inventory all 319 legacy references across 107 Builder, Query
Builder, story, test, and design-system files.
- [x] Classify every reference as a direct semantic replacement, local
composite recipe, documented literal exception, or removable code.
- [x] Record the intentional authored-content, external-preview,
functional-preview, data-visualization, brand, and source-asset
exceptions.
- [x] Treat the public semantic vocabulary as closed by default and
justify any proposed addition by cross-feature reuse and accessibility
need.
- [x] Keep Builder code limited to typed public variables accepted by
`cssVar()`.
### Theme infrastructure
- [x] Add `system` (default), `light`, and `dark` application
preferences.
- [x] Persist the preference in existing client settings and resolve
`system` through `matchMedia`.
- [x] Apply the resolved scheme before paint and respond to
operating-system changes.
- [x] Expose the persisted theme control under **View → Color scheme**.
- [x] Keep authored canvas documents independent from the application
theme.
- [x] Ensure portals, overlays, embedded surfaces, and third-party
controls receive the correct interface scheme.
### Generic interface migration
- [x] Migrate shared dialogs, project settings, notifications, controls,
and Query Builder.
- [x] Migrate dashboard, authentication, publishing, and shared
application UI.
- [x] Migrate the Builder loading state, topbar, sidebars, navigator,
workspace, footer, settings, pages, alerts, and menus.
- [x] Replace fixed interface literals alongside their owning surfaces.
- [x] Remove the old scrollbar variable and hardcoded loading color.
### Style panel and style-source recipes
- [x] Migrate ordinary style-panel inputs, labels, layouts, spacing,
alignment, menus, property states, and value editors.
- [x] Model style-source provenance, kind, interaction, and error as
separate axes.
- [x] Compose categorical style-source colors from neutral and
accent-relative colors.
- [x] Use negative semantics only for actual errors, not overwritten
provenance.
- [x] Reuse one feature-owned recipe across style-source controls,
badges, menus, and values without adding global tokens.
### Canvas and editors
- [x] Migrate canvas selection, reusable outlines, grid guides, resize
handles, labels, badges, and overlays.
- [x] Keep collaborator colors as data visualization while guaranteeing
readable labels.
- [x] Prevent Builder theme values from leaking into authored content.
- [x] Migrate editor surfaces, selections, autocomplete, lint states,
and tooltips.
- [x] Derive syntax visualization from neutral and accent-relative
colors; reserve intent colors for diagnostics.
### Explicit color domains
- [x] Isolate external search and social previews from the application
scheme.
- [x] Preserve user-authored CSS values, functional color previews, and
checkerboards.
- [x] Review brand surfaces independently in both schemes.
- [x] Document every retained literal in the exception ledger.
### Stories, tests, and legacy removal
- [x] Migrate all affected Builder stories and tests.
- [x] Replace the design-system test that intentionally references the
legacy scale.
- [x] Add theme-preference and scheme-resolution behavior tests.
- [x] Remove the legacy `design-tokens.color` registry.
- [x] Remove the Stitches `colors` scale and `rawTheme.colors`.
- [x] Remove color-name conversion code that exists only for the legacy
scale.
- [x] Add a focused repository check that rejects legacy references.
- [x] Confirm no compatibility mappings or deprecated aliases remain.
### Browser-test infrastructure
- [x] Run pure Design System tests in Node and browser-dependent tests
in Vitest Browser Mode with Playwright Chromium.
- [x] Migrate all 79 Builder E2E workflows from the custom harness to
standard Playwright Test.
- [x] Replace filename-based shard discovery with standard Playwright
sharding.
- [x] Preserve clean-backend shard retries and upload Playwright failure
artifacts in CI.
- [x] Remove the custom runner, registry, and timeout infrastructure.
### Verification and documentation
- [x] Compare light-mode visual regressions directly with `main`; review
every difference rather than accepting snapshots wholesale.
- [x] Add representative dark coverage for dashboard, Builder shell,
style panel, dialogs, editors, canvas tools, and status states.
- [x] Test default and extreme custom-theme presets.
- [x] Verify semantic and categorical-recipe contrast.
- [x] Test first paint, persistence, system changes, portals, and canvas
isolation.
- [x] Run affected tests, typechecks, lint, formatting, and Storybook
checks.
- [x] Review fixture impact; no fixture inputs or generated outputs are
affected.
- [x] Review authoritative `docs/` content and update it where needed.
- [x] Review the final diff and make this checklist reflect delivered
scope.
## Current verification
- Design-system typecheck and generated-type freshness: passed.
- Design-system tests: 222 passed, including the full theme-parameter
contrast sweep and browser component coverage.
- Builder typecheck and production build: passed.
- Theme preference and scheme-resolution tests: 10 passed.
- Scoped Oxlint, formatting, and diff checks: passed.
- Design-system Storybook build and representative light/dark visual
review: passed.
- Builder E2E: 80/80 Playwright entries passed (setup plus 79
workflows).
Ref #3818Closes#6267
## Summary
- Make every CSS property in the current MDN dataset available in the
Advanced style workflow, regardless of standards status.
- Mark experimental, nonstandard, and obsolete properties with a yellow
warning icon, status-specific tooltip, and MDN link.
- Add complete CSS anchor-positioning property and value support.
- Update `mdn-data` from 2.28.0 to 2.33.0 and `css-tree` from 3.1.0 to
3.2.1.
## Root cause
Webstudio generated its supported property list from MDN data but
filtered by status and then manually allowed a few experimental and
nonstandard entries. A property could therefore exist in the installed
MDN package without being exposed by the Builder.
Runtime validation had a separate grammar gap. CSS-tree 3.2.1 now
includes the anchor functions and alignment values, but its bundled data
predates the current `position-anchor: normal | match-parent` additions.
## Implementation
- Remove status-based property filtering and the nonstandard-property
allowlist.
- Generate one property-to-status map for 486 standard, 40 experimental,
119 nonstandard, and 15 obsolete properties.
- Show status-specific browser-compatibility guidance for every
non-standard status. Active declarations keep the warning after the
value with 3px left spacing.
- Infer MDN links from existing property metadata and fall back to MDN
search for entries without a direct URL.
- Preserve known vendor prefixes from the generated property inventory
instead of maintaining a prefix allowlist.
- Generate WebKit text fill/stroke longhands directly from MDN instead
of preserving them through parser exceptions.
- Preserve unsupported initial-value shapes as unparsed CSS rather than
excluding their properties.
- Use CSS-tree 3.2.1's native anchor-function and alignment grammars.
Keep one narrow extension for the current `position-anchor` keywords
missing from its bundled MDN data.
- Generate recursively flattened shorthand longhands and initial values
from MDN.
- Expand syntax-defined shorthands generically from CSS-tree's matched
property captures, including function-valued components.
- Decline generic expansion when CSS-tree and MDN disagree about the
longhand set or a longhand repeats.
- Update the font grammar reference and CSS math validation for CSS-tree
3.2.1 compatibility.
- Regenerate CSS metadata and property-variable fixtures.
## Checklist
- [x] Include MDN properties regardless of status.
- [x] Generate status metadata for standard, experimental, nonstandard,
and obsolete properties.
- [x] Show warning icons in Add styles results and active declarations.
- [x] Add status-specific consequences and MDN links.
- [x] Preserve generated vendor-prefixed properties during CSS import.
- [x] Generate WebKit text fill/stroke longhands without parser
exceptions.
- [x] Support `anchor()`, `anchor-size()`, and `anchor-center`.
- [x] Support current `position-anchor` keywords.
- [x] Expand syntax-compatible shorthands through generic MDN metadata
and CSS-tree captures.
- [x] Cover ordered optional, unordered, slash-separated,
function-valued, omitted, CSS-wide, variable, invalid, and
incompatible-data cases.
- [x] Verify new status, warning, prefix, and shorthand tests fail
without their implementations and pass when restored.
- [x] Update to CSS-tree 3.2.1.
- [x] Regenerate CSS metadata and property-variable fixtures.
- [x] Review documentation impact; no authoritative Builder
documentation lists individual supported CSS properties, so no update is
needed.
- [x] Review fixture impact; 668 fixtures and 2,030 valid cases were
regenerated with zero unsupported.
## Verification
- `pnpm --filter @webstudio-is/css-data test` — 4,618 passed, 1 skipped.
- Builder CSS editor and property-status tests — 25 passed.
- CSS data, CSS engine, Project Build, and Builder typechecks passed.
- Lint, formatting, repeated regeneration, and diff checks passed.
## Known limitation
This change covers CSS declarations and values. It does not add a
dedicated Builder editor for the `@position-try` at-rule.
Closes#6162
**Summary**
Validates CSS custom property values as raw CSS token streams before
saving/parsing them.
This prevents malformed values like `#sd'` from being normalized into
`sd` or serialized into broken canvas CSS that invalidates the whole
generated rule.
**Changes**
- Added tokenizer-based validation for custom property values.
- Rejects malformed strings, URLs, comments, unmatched blocks, and
trailing backslashes.
- Preserves raw custom property declaration values before `css-tree` can
repair/normalize them.
- Prevents custom property value editing from coercing invalid input
through fallback parsing like `kebabCase()` or color recovery.
- Added regression tests for advanced panel input and CSS data parsing.
**Verification**
- `pnpm --filter @webstudio-is/css-data test -- parse-css-value.test.ts
parse-css.test.ts`
- `pnpm --filter @webstudio-is/builder test --
parse-intermediate-or-invalid-value.test.ts parse-style-input.test.ts`
## Summary
- Preserve browser-valid CSS math values during CSS value parsing when
`css-tree` can parse the value but cannot fully validate the property
syntax.
- Fix pasted HTML/imported inline styles like nested `clamp()` +
`calc()` font sizes being marked invalid.
- Keep malformed math values and numeric functions in non-numeric
properties invalid.
- Add regression coverage for CSS value parsing and HTML paste/import
behavior.
## Testing
- `pnpm --filter @webstudio-is/css-data test
src/parse-css-value.test.ts`
- `pnpm --filter @webstudio-is/builder test app/shared/html.test.tsx
app/shared/tailwind/tailwind.test.tsx`
## Notes
This prevents valid pasted CSS math expressions from becoming invalid
style values and crashing the builder on import.
ref
https://github.com/webstudio-is/webstudio/issues/3574#issuecomment-4116312483
(e.g. Taillwind's rgb(r g b / var(--tw-opacity)))
- css-engine/schema: extend ColorValue.alpha to number | VarValue
- css-engine/to-value: serialize VarValue alpha using toValue()
- css-data/parse-css-value: add parseColorWithVarAlpha using css-tree
AST substitution (no fragile string replacement); inject fallback: 1
when var has no explicit CSS fallback
- style-object-model: resolve VarValue alpha in substituteVars so the
color picker receives a number when the custom property is defined
- color-control: use VarValue.fallback for the picker when alpha is
still unresolved, falling back to 1 only as a last resort
Ref https://github.com/webstudio-is/webstudio/issues/3574
Here improved css value parser to parse and store any color space.
oklch(), hsl(), color() and others can be used in any color input. Color
picker converts to rgb on the fly and do not support any other color
space at the moment.
---------
Co-authored-by: Oleg Isonen <oleg008@gmail.com>
Ref https://github.com/webstudio-is/webstudio/issues/3399
Here added support for computing css variables inside layers and tuples
to support cases like
```css
transition-timing-function: var(--ease), var(--ease-in);
```
Ref https://github.com/webstudio-is/webstudio/issues/3399
Initially css variable fallback was defined in schema as array though
it's actually a single value by spec and may contain commas. We don't
have css variables in data yet so should be safe to change schema before
too late.
## Description
This PR allows to parse the `perspective-origin` property value as
`tuple`. And added test cases for different possible values.
## Steps for reproduction
- Allows to parse `perspective-origin` values as tuples.
## Code Review
- [ ] hi @TrySound , I need you to do
- conceptual review (architecture, feature-correctness)
- detailed review (read every line)
- test it on preview
## Before requesting a review
- [x] made a self-review
- [x] added inline comments where things may be not obvious (the "why",
not "what")
## Before merging
- [x] tested locally and on preview environment (preview dev login:
5de6)
- [x] updated [test
cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md)
document
- [x] added tests
## Description
The current parser is not able to parse all the possible values for
`transform-origin` property. The property takes `single`, `double` and
`triple` values. So these need to be parsed as `tuples` to support
`triple` values now.
## Code Review
- [ ] hi @TrySound , I need you to do
- conceptual review (architecture, feature-correctness)
- detailed review (read every line)
- test it on preview
## Before requesting a review
- [x] made a self-review
## Before merging
- [x] tested locally and on preview environment (preview dev login:
5de6)
- [x] updated [test
cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md)
document
- [x] added tests
Here refactored value parser with literal parser
which can be reused in all functions, tuples and layers.
Color parser now relies on specific ast types.
Tuples are generated for all values with multiple nodes.
This all will let us build more universal parsers with less effort.
## Description
Adds support for parsing `scale` css property. Will be used in bringing
support for transforms ection in style panel.
## Code Review
- [ ] hi @TrySound , I need you to do
- conceptual review (architecture, feature-correctness)
- detailed review (read every line)
- test it on preview
## Before requesting a review
- [x] made a self-review
- [x] added inline comments where things may be not obvious (the "why",
not "what")
## Before merging
- [x] tested locally and on preview environment (preview dev login:
5de6)
- [ x added tests
## Description
Parses `translate` css property and converts them into tuples. Related
to the series of PR that enables transform property #3411
## Code Review
- [ ] hi @kof, I need you to do
- conceptual review (architecture, feature-correctness)
- detailed review (read every line)
- test it on preview
## Before requesting a review
- [x] made a self-review
## Before merging
- [x] tested locally and on preview environment (preview dev login:
5de6)
- [x] added tests
## Description
Parse `transform` value into tuples. We need to use shorthand transform
property for `skew` and `rotate` to support the UX.
- **skew** doesn't have individual property yet. So need to use this as
backup.
- **rotate** doesn support setting individual `x, y, z` properties on
the long hand property like in the design. So, we are can use transforms
as backup and allow to set individual `rotateX`, `rotateY` and `rotateZ`
values.
## Code Review
- [ ] hi @TrySound , I need you to do
- detailed review (read every line)
## Before requesting a review
- [x] made a self-review
## Before merging
- [x] tested locally and on preview environment (preview dev login:
5de6)
- [x] updated [test
cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md)
document
- [x] added tests
All value parsers can be generalized at some point to make adding new
feature easier. Here moved timing-function parser into value parser and
reused it in builder.
Ref https://github.com/webstudio-is/webstudio/issues/2647
Here improved our value parser to not output invalid type every now and
then and just represent unknown properties as unparsed. This will let us
parse it in the future without loosing anything on paste.
Here added universal support for css wide keywords. initial, inherit and
unset will be parsed the same way for layered properties and singular
properties.
It will never be part of layers and always be represented as root
keyword.
Ref https://github.com/webstudio-is/webstudio/issues/2647
Here replaced shorthand background-position with
background-position-x and background-position-y
Added shorthand expander, migration and merger
for css engine to improve browser support for offsets.
Ref https://github.com/webstudio-is/webstudio/issues/2647
Here moved the logic to parse values as layers
from parseCss to parseCssValue utilities
so we could get correct values for example when expand shorthands in
migrations or try to parse unparsed values.
We duplicate the logic for parsing css for ai and webflow. Here a bit of
cleanup to simplify further work on shorthands.
Also upgraded unocss and got rid of bug workaround (fixed in unocss)
## Description
fixes#3355
Allows filter parser to parse `rgba` and `hex` values when copy pasted.
I added some tests for filters too, we added initiall before as the css
filters parser returns false positives. But now, i added all the valid
tests, so at least we can catch things like this in future.
## Steps for reproduction
- Use the following filter values to test them.
```
drop-shadow(10px 10px 25px blue)
drop-shadow(10px 10px 25px rgba(0, 0, 255, 1))
drop-shadow(10px 10px 25px #0000FF)
```
## Code Review
- [ ] hi @kof, I need you to do
- conceptual review (architecture, feature-correctness)
- detailed review (read every line)
- test it on preview
## Before requesting a review
- [x] made a self-review
## Before merging
- [x] tested locally and on preview environment (preview dev login:
5de6)
- [x] added tests
Closes https://github.com/webstudio-is/webstudio/issues/3203
If DOM parser is available, let it do the parsing and don't rely on
csstree if possible for validation
## Steps for reproduction
1. click button
2. expect xyz
## Code Review
- [ ] hi @kof, I need you to do
- conceptual review (architecture, feature-correctness)
- detailed review (read every line)
- test it on preview
## Before requesting a review
- [ ] made a self-review
- [ ] added inline comments where things may be not obvious (the "why",
not "what")
## Before merging
- [ ] tested locally and on preview environment (preview dev login:
5de6)
- [ ] updated [test
cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md)
document
- [ ] added tests
- [ ] if any new env variables are added, added them to `.env.example`
and the `builder/env-check.js` if mandatory
## Description
1. Add support of simple tuple types to the parseCssValue
2. Add background properties conversion
## Steps for reproduction
see tests
## Code Review
- [ ] hi @kof, I need you to do
- conceptual review (architecture, feature-correctness)
- detailed review (read every line)
- test it on preview
## Before requesting a review
- [ ] made a self-review
- [ ] added inline comments where things may be not obvious (the "why",
not "what")
## Before merging
- [ ] tested locally and on preview environment (preview dev login:
5de6)
- [ ] updated [test
cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md)
document
- [ ] added tests
- [ ] if any new env variables are added, added them to `.env.example`
and the `builder/env-check.js` if mandatory
## Description
This PR moves the parse-css-value utility that was before part of the
builder app to the css-data package.
In this package we are adding an initial `parse-css` utility that will take
CSS and convert to a format that we can work on within Webstudio. For
now it is relatively simple and it supports only CSS classes parsing.