UNPKG

@wix/css-property-parser

Version:

A comprehensive TypeScript library for parsing and serializing CSS property values with full MDN specification compliance

582 lines (478 loc) โ€ข 24.9 kB
# CSS Property Parser A comprehensive TypeScript library for parsing and serializing CSS property values with full MDN specification compliance. ## ๐ŸŒ [**Try the Interactive Demo โ†’**](https://wix-incubator.github.io/css-property-parser/) Test all CSS property parsers live in your browser with real-time parsing and examples. ## ๐Ÿš€ Features - **124 CSS Property Evaluators** - Complete parsing support for layout, typography, colors, and more - **Dual Package Support** - Works with both ESM (`import`) and CommonJS (`require`) - **MDN Compliant** - Follows official CSS specifications exactly - **TypeScript First** - Full type safety with comprehensive type definitions for all CSS values - **Round-Trip Stable** - Parse โ†’ serialize โ†’ parse produces identical results - **CSS Functions** - Supports `calc()`, `min()`, `max()`, `clamp()`, gradients, and more - **CSS Variables** - Handles custom properties with fallback values - **SPX Units** - Supports Wix's SPX (Scalable-Pixel) units alongside standard CSS units - **Performance Optimized** - Fast parsing with minimal memory footprint - **8,428+ Tests** - Comprehensive test coverage across 132 test suites with 100% pass rate - **Shared Evaluator Pattern** - 95% code reduction through intelligent code reuse ## ๐Ÿ“ฆ Installation ```bash npm install @wix/css-property-parser ``` ## ๐ŸŽฏ Usage ### ESM (Import) - Recommended ```typescript import { Width, Height, Margin, Background, Font } from '@wix/css-property-parser'; // Parse CSS values const width = Width.parse('calc(100% - 20px)'); const background = Background.parse('linear-gradient(45deg, red, blue)'); // Serialize back to CSS const widthCSS = Width.toCSSValue(width); // "calc(100% - 20px)" const bgCSS = Background.toCSSValue(background); // "linear-gradient(45deg, red, blue)" ``` ### CommonJS (Require) ```javascript const { Width, Height, Margin, Background, Font } = require('@wix/css-property-parser'); // Parse CSS values const width = Width.parse('calc(100% - 20px)'); const background = Background.parse('linear-gradient(45deg, red, blue)'); // Serialize back to CSS const widthCSS = Width.toCSSValue(width); // "calc(100% - 20px)" const bgCSS = Background.toCSSValue(background); // "linear-gradient(45deg, red, blue)" ``` ### TypeScript Types ```typescript import { Width, Height, Background, Font, Margin, Color, Position, // Import types for parsed values WidthValue, HeightValue, BackgroundValue, FontValue, MarginValue, ColorValue, PositionValue, LengthPercentageValue, // Import atomic types CSSLengthValue, CSSPercentageValue, LengthValue, PercentageValue } from '@wix/css-property-parser'; // Use types for variables const width: WidthValue | null = Width.parse('100px'); const background: BackgroundValue | null = Background.parse('red'); const margin: MarginValue | null = Margin.parse('10px 20px'); const color: ColorValue | null = Color.parse('#ff0000'); const position: PositionValue | null = Position.parse('absolute'); // Type-safe functions function processWidth(value: string): WidthValue | null { return Width.parse(value); } function serializeFont(font: FontValue): string { return Font.toCSSValue(font) || ''; } // All types are available from the main package for convenience // This includes both property value types and atomic CSS types ``` ## ๐ŸŽฏ Advanced Examples > **Note**: All examples work with both ESM (`import`) and CommonJS (`require`). For TypeScript type imports, see [Usage](#-usage) section above. ```typescript import { Width, Height, Margin, Background, Font } from '@wix/css-property-parser'; // Parse CSS values with advanced features const width = Width.parse('calc(100% - 20px)'); const height = Height.parse('50vh'); const margin = Margin.parse('10spx 20spx'); // SPX (Scalable-Pixel) support const background = Background.parse('linear-gradient(45deg, red, blue)'); const font = Font.parse('bold 16px/1.5 "Helvetica Neue", Arial, sans-serif'); // Serialize back to CSS const widthCSS = Width.toCSSValue(width); // "calc(100% - 20px)" const heightCSS = Height.toCSSValue(height); // "50vh" const marginCSS = Margin.toCSSValue(margin); // "10spx 20spx" const bgCSS = Background.toCSSValue(background); // "linear-gradient(45deg, red, blue)" const fontCSS = Font.toCSSValue(font); // "bold 16px/1.5 "Helvetica Neue", Arial, sans-serif" ``` ## ๐Ÿ“‹ API Reference ### Core API Pattern Every property evaluator follows the same consistent API: ```typescript interface PropertyEvaluator<T> { parse(value: string): T | null; toCSSValue(parsed: T | null): string | null; } ``` ### Property Evaluators #### Layout Properties ```typescript import { Width, Height, MinWidth, MaxWidth, MinHeight, MaxHeight } from '@wix/css-property-parser'; // Sizing properties support lengths, percentages, and keywords const width = Width.parse('50%'); // { value: 50, unit: '%' } const height = Height.parse('auto'); // { keyword: 'auto' } const minWidth = MinWidth.parse('0'); // { value: 0, unit: 'px' } ``` #### Spacing Properties ```typescript import { Margin, Padding, Gap, ColumnGap, RowGap } from '@wix/css-property-parser'; // Shorthand properties expand to constituent parts const margin = Margin.parse('10px 20px'); // Expands to top/right/bottom/left const padding = Padding.parse('1em'); // Expands to all sides const gap = Gap.parse('10px 20px'); // Row and column gaps ``` #### Typography Properties ```typescript import { FontSize, FontWeight, FontFamily, LineHeight, LetterSpacing } from '@wix/css-property-parser'; // Font properties with comprehensive keyword support const fontSize = FontSize.parse('1.2em'); // { type: 'length', value: 1.2, unit: 'em' } const fontWeight = FontWeight.parse('bold'); // { type: 'keyword', keyword: 'bold' } const fontFamily = FontFamily.parse('Arial, sans-serif'); // { type: 'font-list', families: ['Arial', 'sans-serif'] } const lineHeight = LineHeight.parse('1.5'); // { type: 'number', value: 1.5 } (unitless) ``` #### Complex Properties ```typescript import { Background, Border, Font, TextShadow } from '@wix/css-property-parser'; // Multi-value and shorthand properties const background = Background.parse('url(bg.jpg) no-repeat center / cover'); const border = Border.parse('2px solid red'); const font = Font.parse('bold 16px/1.4 "Helvetica Neue", Arial, sans-serif'); const textShadow = TextShadow.parse('2px 2px 4px rgba(0,0,0,0.5)'); ``` ### Type Evaluators For parsing individual CSS data types: ```typescript import { LengthEvaluator, PercentageEvaluator, Color, AngleEvaluator, NumberEvaluator } from '@wix/css-property-parser'; // Parse individual CSS types const length = LengthEvaluator.parse('10px'); // { value: 10, unit: 'px' } const percentage = PercentageEvaluator.parse('50%'); // { value: 50, unit: '%' } const color = Color.parse('#ff0000'); // { type: 'hex', value: 'ff0000' } const angle = AngleEvaluator.parse('45deg'); // { value: 45, unit: 'deg' } ``` ## ๐Ÿ”ง Advanced Usage ### CSS Variables ```typescript import { Width, CssVariable } from '@wix/css-property-parser'; // CSS variables are parsed but not resolved const width = Width.parse('var(--main-width)'); // Returns: { CSSvariable: '--main-width' } const widthWithFallback = Width.parse('var(--main-width, 100px)'); // Returns: { CSSvariable: '--main-width', defaultValue: '100px' } ``` ### CSS Functions ```typescript import { Width, FontSize } from '@wix/css-property-parser'; // Calc expressions const width = Width.parse('calc(100% - 20px)'); // Returns: { expression: '100% - 20px', function: 'calc' } // Min/max/clamp functions const fontSize = FontSize.parse('clamp(1rem, 2.5vw, 2rem)'); // Returns: { expression: '1rem, 2.5vw, 2rem', function: 'clamp' } ``` ### Round-Trip Stability ```typescript import { Background } from '@wix/css-property-parser'; const originalValue = 'linear-gradient(45deg, red 0%, blue 100%)'; const parsed = Background.parse(originalValue); const serialized = Background.toCSSValue(parsed); console.log(serialized === originalValue); // true ``` ## ๐ŸŽจ Use Cases ### Design Systems ```typescript import { FontSize, Color, Margin } from '@wix/css-property-parser'; // Validate design tokens function validateDesignToken(property: string, value: string): boolean { switch (property) { case 'font-size': return FontSize.parse(value) !== null; case 'color': return Color.parse(value) !== null; case 'margin': return Margin.parse(value) !== null; default: return false; } } ``` ### CSS-in-JS Libraries ```typescript import { Width, Height, Background } from '@wix/css-property-parser'; // Parse and transform CSS values function processStyles(styles: Record<string, string>) { const processed: Record<string, any> = {}; for (const [property, value] of Object.entries(styles)) { switch (property) { case 'width': processed.width = Width.parse(value); break; case 'height': processed.height = Height.parse(value); break; case 'background': processed.background = Background.parse(value); break; } } return processed; } ``` ### CSS Optimization Tools ```typescript import { Color, Background } from '@wix/css-property-parser'; // Optimize CSS values function optimizeColor(value: string): string { const parsed = Color.parse(value); if (parsed && parsed.type === 'hex') { // Convert to shortest hex format return Color.toCSSValue(parsed); } return value; } ``` ## ๐Ÿ“š Documentation - **[Evaluator Guide](docs/EVALUATOR_GUIDE.md)** - Comprehensive guide for creating new property evaluators - **[Pre-Commit Hook](docs/PRE_COMMIT_HOOK.md)** - Development workflow automation ## ๐Ÿงช Testing ```bash # Run all tests npm test # Run tests for specific property npm test -- width.test.ts # Run tests with coverage npm test:coverage # Watch mode for development npm test:watch ``` ## ๐Ÿ—๏ธ Development ```bash # Install dependencies npm install # Build TypeScript npm run build # Build with watch mode npm run build:watch # Run development server npm run dev ``` ## ๐Ÿ“Š Supported Properties **Total: 124 CSS Property Evaluators** - All evaluators include full MDN compliance, comprehensive test coverage, and TypeScript type definitions. > ๐Ÿ’ก **Quick Reference**: Use `{PropertyName}.parse(value)` to parse and `{PropertyName}.toCSSValue(parsed)` to serialize any property below. ### ๐Ÿ”ง Data Types & Base Types (10 evaluators) - **Primitive Types**: `angle`, `color`, `length`, `number`, `percentage`, `string`, `time` - **Composite Types**: `length-percentage`, `css-variable`, `blend-mode` ### ๐Ÿ“ Sizing Properties (12 evaluators) - **Standard Box Model**: `width`, `height`, `min-width`, `max-width`, `min-height`, `max-height` - **CSS Logical Properties**: `block-size`, `inline-size`, `min-block-size`, `max-block-size`, `min-inline-size`, `max-inline-size` ### ๐Ÿ“ Spacing Properties (17 evaluators) - **Gap Properties** (3): `gap`, `column-gap`, `row-gap` - **Margin Properties** (7): `margin` (shorthand), `margin-top`, `margin-right`, `margin-bottom`, `margin-left`, `margin-inline-start`, `margin-inline-end` - **Padding Properties** (7): `padding` (shorthand), `padding-top`, `padding-right`, `padding-bottom`, `padding-left`, `padding-inline-start`, `padding-inline-end` ### ๐Ÿ”ฒ Border Properties (43 evaluators) - **Border Shorthands** (4): `border`, `border-color`, `border-style`, `border-width` - **Border Radius** (5): `border-radius`, `border-start-start-radius`, `border-start-end-radius`, `border-end-start-radius`, `border-end-end-radius` - **Physical Borders** (16): - Shorthands: `border-top`, `border-right`, `border-bottom`, `border-left` - Colors: `border-top-color`, `border-right-color`, `border-bottom-color`, `border-left-color` - Styles: `border-top-style`, `border-right-style`, `border-bottom-style`, `border-left-style` - Widths: `border-top-width`, `border-right-width`, `border-bottom-width`, `border-left-width` - **CSS Logical Block Borders** (9): - Shorthands: `border-block`, `border-block-start`, `border-block-end` - Colors: `border-block-start-color`, `border-block-end-color` - Styles: `border-block-start-style`, `border-block-end-style` - Widths: `border-block-start-width`, `border-block-end-width` - **CSS Logical Inline Borders** (9): - Shorthands: `border-inline`, `border-inline-start`, `border-inline-end` - Colors: `border-inline-start-color`, `border-inline-end-color` - Styles: `border-inline-start-style`, `border-inline-end-style` - Widths: `border-inline-start-width`, `border-inline-end-width` ### ๐Ÿ”ค Typography Properties (18 evaluators) - **Font Properties** (7): `font` (shorthand), `font-family`, `font-size`, `font-weight`, `font-style`, `font-variant`, `font-stretch` - **Text Properties** (6): `text-align`, `text-transform`, `text-decoration`, `text-shadow`, `text-overflow`, `text-indent` - **Text Spacing** (2): `line-height`, `letter-spacing` - **Text Flow** (4): `white-space`, `word-break`, `overflow-wrap`, `writing-mode` ### ๐Ÿ—๏ธ Layout & Grid Properties (12 evaluators) - **CSS Grid Template** (3): `grid-template` (shorthand), `grid-template-columns`, `grid-template-rows` - **CSS Grid Areas** (3): `grid-area`, `grid-column`, `grid-row` - **CSS Grid Gaps** (3): `grid-gap` (shorthand), `grid-column-gap`, `grid-row-gap` - **Layout Control** (3): `display`, `overflow`, `visibility` ### ๐ŸŽจ Visual Properties (8 evaluators) - **Colors & Effects** (4): `background`, `color`, `opacity`, `box-shadow` - **Object Properties** (2): `object-fit`, `object-position` - **Positioning** (2): `position`, `z-index` ### โšก Layout Alignment (4 evaluators) - **Flexbox & Grid Alignment** (4): `align-items`, `align-self`, `justify-content`, `flex-direction` ### ๐Ÿ”„ Advanced Features - **Shared Evaluator Pattern** (8 shared): 95% code reduction through intelligent reuse - Sizing properties: width/height variants - Spacing properties: gap, margin, padding variants - Border width properties: all border-width variants - Border style properties: all border-style variants - Border color properties: all border-color variants - Border individual properties: border shorthand variants - Logical border properties: block/inline border variants - Logical border radius properties: corner radius variants - **CSS Functions**: Full support for `calc()`, `min()`, `max()`, `clamp()`, `var()`, gradients - **SPX Units**: Wix Scalable-Pixel units for responsive design - **Round-Trip Stability**: Parse โ†’ serialize โ†’ parse produces identical results - **Performance**: Optimized parsing with minimal memory footprint ## ๐Ÿค Contributing 1. Fork the repository 2. Create a feature branch 3. Add comprehensive tests (20+ tests minimum) 4. Ensure all tests pass 5. Follow the [Evaluator Guide](docs/EVALUATOR_GUIDE.md) 6. Submit a pull request ## ๐Ÿ“„ License ISC License - see LICENSE file for details. ## ๐Ÿ™ Acknowledgments - Built with MDN CSS specifications as the authoritative reference - Inspired by the need for robust CSS parsing in modern web applications - Comprehensive testing ensures production-ready reliability - Enhanced and documented with assistance from Claude (Anthropic AI) --- ## ๐Ÿ“‹ Complete Evaluator Exports Reference All evaluators are available as named exports from the main package. Use `import { EvaluatorName } from '@wix/css-property-parser'` or `const { EvaluatorName } = require('@wix/css-property-parser')`. ### ๐Ÿ”ง Type & Utility Evaluators | Export Name | CSS Property/Type | Description | |-------------|-------------------|-------------| | `AngleEvaluator` | `<angle>` | CSS angle values (deg, rad, turn, grad) | | `BlendMode` | `mix-blend-mode`, `background-blend-mode` | CSS blend mode keywords | | `Color` | `<color>` | CSS color values (hex, rgb, hsl, named, etc.) | | `CssVariable` | `var()` | CSS custom properties and variables | | `LengthEvaluator` | `<length>` | CSS length values (px, em, rem, vh, etc.) | | `LengthPercentage` | `<length-percentage>` | Combined length and percentage values | | `NumberEvaluator` | `<number>` | CSS numeric values | | `PercentageEvaluator` | `<percentage>` | CSS percentage values | | `Position` | `position` | CSS position keywords (static, relative, absolute, fixed, sticky) | | `StringEvaluator` | `<string>` | CSS string values | | `TimeEvaluator` | `<time>` | CSS time values (s, ms) | ### ๐Ÿ“ Sizing Properties | Export Name | CSS Property | Description | |-------------|--------------|-------------| | `BlockSize` | `block-size` | Logical block dimension | | `Height` | `height` | Element height | | `InlineSize` | `inline-size` | Logical inline dimension | | `MaxBlockSize` | `max-block-size` | Maximum logical block size | | `MaxHeight` | `max-height` | Maximum element height | | `MaxInlineSize` | `max-inline-size` | Maximum logical inline size | | `MaxWidth` | `max-width` | Maximum element width | | `MinBlockSize` | `min-block-size` | Minimum logical block size | | `MinHeight` | `min-height` | Minimum element height | | `MinInlineSize` | `min-inline-size` | Minimum logical inline size | | `MinWidth` | `min-width` | Minimum element width | | `Width` | `width` | Element width | ### ๐Ÿ“ Spacing Properties | Export Name | CSS Property | Description | |-------------|--------------|-------------| | `ColumnGap` | `column-gap` | Gap between columns | | `Gap` | `gap` | Gap shorthand (row and column) | | `Margin` | `margin` | Margin shorthand (all sides) | | `MarginBottom` | `margin-bottom` | Bottom margin | | `MarginInlineEnd` | `margin-inline-end` | Logical inline end margin | | `MarginInlineStart` | `margin-inline-start` | Logical inline start margin | | `MarginLeft` | `margin-left` | Left margin | | `MarginRight` | `margin-right` | Right margin | | `MarginTop` | `margin-top` | Top margin | | `Padding` | `padding` | Padding shorthand (all sides) | | `PaddingBottom` | `padding-bottom` | Bottom padding | | `PaddingInlineEnd` | `padding-inline-end` | Logical inline end padding | | `PaddingInlineStart` | `padding-inline-start` | Logical inline start padding | | `PaddingLeft` | `padding-left` | Left padding | | `PaddingRight` | `padding-right` | Right padding | | `PaddingTop` | `padding-top` | Top padding | | `RowGap` | `row-gap` | Gap between rows | ### ๐Ÿ”ฒ Border Properties | Export Name | CSS Property | Description | |-------------|--------------|-------------| | `Border` | `border` | Border shorthand (all sides) | | `BorderBlock` | `border-block` | Logical block borders | | `BorderBlockEnd` | `border-block-end` | Logical block end border | | `BorderBlockEndColor` | `border-block-end-color` | Logical block end border color | | `BorderBlockEndStyle` | `border-block-end-style` | Logical block end border style | | `BorderBlockEndWidth` | `border-block-end-width` | Logical block end border width | | `BorderBlockStart` | `border-block-start` | Logical block start border | | `BorderBlockStartColor` | `border-block-start-color` | Logical block start border color | | `BorderBlockStartStyle` | `border-block-start-style` | Logical block start border style | | `BorderBlockStartWidth` | `border-block-start-width` | Logical block start border width | | `BorderBottom` | `border-bottom` | Bottom border | | `BorderBottomColor` | `border-bottom-color` | Bottom border color | | `BorderBottomStyle` | `border-bottom-style` | Bottom border style | | `BorderBottomWidth` | `border-bottom-width` | Bottom border width | | `BorderColor` | `border-color` | Border color shorthand | | `BorderEndEndRadius` | `border-end-end-radius` | Logical end-end corner radius | | `BorderEndStartRadius` | `border-end-start-radius` | Logical end-start corner radius | | `BorderInline` | `border-inline` | Logical inline borders | | `BorderInlineEnd` | `border-inline-end` | Logical inline end border | | `BorderInlineEndColor` | `border-inline-end-color` | Logical inline end border color | | `BorderInlineEndStyle` | `border-inline-end-style` | Logical inline end border style | | `BorderInlineEndWidth` | `border-inline-end-width` | Logical inline end border width | | `BorderInlineStart` | `border-inline-start` | Logical inline start border | | `BorderInlineStartColor` | `border-inline-start-color` | Logical inline start border color | | `BorderInlineStartStyle` | `border-inline-start-style` | Logical inline start border style | | `BorderInlineStartWidth` | `border-inline-start-width` | Logical inline start border width | | `BorderLeft` | `border-left` | Left border | | `BorderLeftColor` | `border-left-color` | Left border color | | `BorderLeftStyle` | `border-left-style` | Left border style | | `BorderLeftWidth` | `border-left-width` | Left border width | | `BorderRadius` | `border-radius` | Border radius shorthand | | `BorderRight` | `border-right` | Right border | | `BorderRightColor` | `border-right-color` | Right border color | | `BorderRightStyle` | `border-right-style` | Right border style | | `BorderRightWidth` | `border-right-width` | Right border width | | `BorderStartEndRadius` | `border-start-end-radius` | Logical start-end corner radius | | `BorderStartStartRadius` | `border-start-start-radius` | Logical start-start corner radius | | `BorderStyle` | `border-style` | Border style shorthand | | `BorderTop` | `border-top` | Top border | | `BorderTopColor` | `border-top-color` | Top border color | | `BorderTopStyle` | `border-top-style` | Top border style | | `BorderTopWidth` | `border-top-width` | Top border width | | `BorderWidth` | `border-width` | Border width shorthand | ### ๐Ÿ”ค Typography Properties | Export Name | CSS Property | Description | |-------------|--------------|-------------| | `Font` | `font` | Font shorthand (family, size, weight, style, etc.) | | `FontFamily` | `font-family` | Font family stack | | `FontSize` | `font-size` | Font size | | `FontStretch` | `font-stretch` | Font stretch/width | | `FontStyle` | `font-style` | Font style (normal, italic, oblique) | | `FontVariant` | `font-variant` | Font variant settings | | `FontWeight` | `font-weight` | Font weight (normal, bold, 100-900) | | `LetterSpacing` | `letter-spacing` | Spacing between characters | | `LineHeight` | `line-height` | Line height/leading | | `TextAlign` | `text-align` | Text alignment | | `TextDecoration` | `text-decoration` | Text decoration (underline, strikethrough, etc.) | | `TextIndent` | `text-indent` | First-line text indentation | | `TextOverflow` | `text-overflow` | Text overflow behavior | | `TextShadow` | `text-shadow` | Text shadow effects | | `TextTransform` | `text-transform` | Text case transformation | | `WhiteSpace` | `white-space` | Whitespace handling | | `WordBreak` | `word-break` | Word breaking behavior | | `WritingMode` | `writing-mode` | Text writing direction | ### ๐Ÿ—๏ธ Layout & Grid Properties | Export Name | CSS Property | Description | |-------------|--------------|-------------| | `Display` | `display` | Display type (block, inline, flex, grid, etc.) | | `GridArea` | `grid-area` | Grid area placement | | `GridColumn` | `grid-column` | Grid column placement | | `GridColumnGap` | `grid-column-gap` | Gap between grid columns | | `GridGap` | `grid-gap` | Grid gap shorthand | | `GridRow` | `grid-row` | Grid row placement | | `GridRowGap` | `grid-row-gap` | Gap between grid rows | | `GridTemplate` | `grid-template` | Grid template shorthand | | `GridTemplateColumns` | `grid-template-columns` | Grid column track definitions | | `GridTemplateRows` | `grid-template-rows` | Grid row track definitions | | `Overflow` | `overflow` | Content overflow behavior | | `OverflowWrap` | `overflow-wrap` | Long word wrapping behavior | ### ๐ŸŽจ Visual Properties | Export Name | CSS Property | Description | |-------------|--------------|-------------| | `Background` | `background` | Background shorthand (image, color, position, etc.) | | `BoxShadow` | `box-shadow` | Box shadow effects | | `ObjectFit` | `object-fit` | Object fitting behavior | | `ObjectPosition` | `object-position` | Object positioning | | `Opacity` | `opacity` | Element opacity/transparency | | `Visibility` | `visibility` | Element visibility | | `ZIndex` | `z-index` | Stacking order | ### โšก Layout Alignment | Export Name | CSS Property | Description | |-------------|--------------|-------------| | `AlignItems` | `align-items` | Cross-axis alignment | | `AlignSelf` | `align-self` | Individual item cross-axis alignment | | `FlexDirection` | `flex-direction` | Flex container direction | | `JustifyContent` | `justify-content` | Main-axis alignment | --- **Total: 124 Evaluator Exports** - All evaluators follow the same API pattern: `parse(value: string)` and `toCSSValue(parsed)` with full TypeScript support.