@tenphi/tasty 0.0.0-snapshot.bbd10c7 → 0.0.0-snapshot.bbfcd75
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.
- package/README.md +196 -140
- package/dist/babel-Zs1Q03qt.d.ts +83 -0
- package/dist/chunks/async-storage-CG5HnUT5.js +20 -0
- package/dist/chunks/async-storage-CG5HnUT5.js.map +1 -0
- package/dist/chunks/build-config-CUQVwdWF.js +42 -0
- package/dist/chunks/build-config-CUQVwdWF.js.map +1 -0
- package/dist/chunks/collector-B0LTHw6Z.js +208 -0
- package/dist/chunks/collector-B0LTHw6Z.js.map +1 -0
- package/dist/chunks/config-engine-C3pF9EK9.js +267 -0
- package/dist/chunks/config-engine-C3pF9EK9.js.map +1 -0
- package/dist/chunks/css-definitions-D_it4XZH.js +875 -0
- package/dist/chunks/css-definitions-D_it4XZH.js.map +1 -0
- package/dist/chunks/css-resources-BN8apgxt.js +142 -0
- package/dist/chunks/css-resources-BN8apgxt.js.map +1 -0
- package/dist/{debug.js → chunks/debug-C9LeNVoJ.js} +187 -139
- package/dist/chunks/debug-C9LeNVoJ.js.map +1 -0
- package/dist/chunks/dsl-C5yuAn41.js +1373 -0
- package/dist/chunks/dsl-C5yuAn41.js.map +1 -0
- package/dist/chunks/hydration-TRcz5vnk.js +41 -0
- package/dist/chunks/hydration-TRcz5vnk.js.map +1 -0
- package/dist/chunks/react-runtime-O-OUrKLH.js +557 -0
- package/dist/chunks/react-runtime-O-OUrKLH.js.map +1 -0
- package/dist/chunks/runtime-engine-DvnE2g-E.js +2437 -0
- package/dist/chunks/runtime-engine-DvnE2g-E.js.map +1 -0
- package/dist/chunks/runtime-state-BTDqSYBk.js +127 -0
- package/dist/chunks/runtime-state-BTDqSYBk.js.map +1 -0
- package/dist/{utils/merge-styles.js → chunks/shared-utils-DWA4LjIa.js} +55 -55
- package/dist/chunks/shared-utils-DWA4LjIa.js.map +1 -0
- package/dist/chunks/style-engine-C1ECBZxR.js +5131 -0
- package/dist/chunks/style-engine-C1ECBZxR.js.map +1 -0
- package/dist/{zero/extractor.js → chunks/zero-engine-B6EnYcXY.js} +89 -82
- package/dist/chunks/zero-engine-B6EnYcXY.js.map +1 -0
- package/dist/collector-hl5KNH7k.d.ts +165 -0
- package/dist/config-DWL1ZYue.d.ts +1358 -0
- package/dist/core/index.d.ts +5 -34
- package/dist/core/index.js +9 -27
- package/dist/index-D9N9GV24.d.ts +1947 -0
- package/dist/index.d.ts +5 -51
- package/dist/index.js +11 -36
- package/dist/{utils/merge-styles.d.ts → merge-styles-C_oMoJVd.d.ts} +3 -3
- package/dist/ssr/astro-client.js +6 -0
- package/dist/ssr/astro-client.js.map +1 -0
- package/dist/ssr/astro-middleware-extract-static.d.ts +11 -0
- package/dist/ssr/astro-middleware-extract-static.js +9 -0
- package/dist/ssr/astro-middleware-extract-static.js.map +1 -0
- package/dist/ssr/astro-middleware-extract.d.ts +11 -0
- package/dist/ssr/astro-middleware-extract.js +9 -0
- package/dist/ssr/astro-middleware-extract.js.map +1 -0
- package/dist/ssr/astro-middleware-static.d.ts +18 -0
- package/dist/ssr/astro-middleware-static.js +7 -0
- package/dist/ssr/astro-middleware-static.js.map +1 -0
- package/dist/ssr/astro-middleware.d.ts +19 -0
- package/dist/ssr/astro-middleware.js +7 -0
- package/dist/ssr/astro-middleware.js.map +1 -0
- package/dist/ssr/astro.d.ts +117 -11
- package/dist/ssr/astro.js +204 -48
- package/dist/ssr/astro.js.map +1 -1
- package/dist/ssr/index.d.ts +37 -4
- package/dist/ssr/index.js +6 -6
- package/dist/ssr/index.js.map +1 -1
- package/dist/ssr/next-config.d.ts +66 -0
- package/dist/ssr/next-config.js +124 -0
- package/dist/ssr/next-config.js.map +1 -0
- package/dist/ssr/next.d.ts +14 -5
- package/dist/ssr/next.js +31 -46
- package/dist/ssr/next.js.map +1 -1
- package/dist/static/index.d.ts +91 -5
- package/dist/static/index.js +31 -3
- package/dist/static/index.js.map +1 -0
- package/dist/static/inject.js.map +1 -1
- package/dist/types-jKZNpias.d.ts +1616 -0
- package/dist/zero/babel.d.ts +2 -182
- package/dist/zero/babel.js +82 -113
- package/dist/zero/babel.js.map +1 -1
- package/dist/zero/index.d.ts +81 -3
- package/dist/zero/index.js +2 -3
- package/dist/zero/next.d.ts +1 -1
- package/dist/zero/next.js +0 -31
- package/dist/zero/next.js.map +1 -1
- package/docs/README.md +20 -12
- package/docs/adoption.md +59 -32
- package/docs/ai-agents.md +229 -0
- package/docs/comparison.md +76 -48
- package/docs/configuration.md +566 -69
- package/docs/debug.md +37 -23
- package/docs/design-system.md +63 -32
- package/docs/dsl.md +527 -179
- package/docs/getting-started.md +62 -41
- package/docs/injector.md +157 -51
- package/docs/methodology.md +69 -37
- package/docs/migration-v3.md +285 -0
- package/docs/pipeline.md +744 -0
- package/docs/plugins.md +353 -0
- package/docs/{runtime.md → react-api.md} +176 -67
- package/docs/runtime-benchmarks.md +377 -0
- package/docs/ssr.md +350 -94
- package/docs/styles.md +108 -27
- package/docs/tasty-static.md +149 -87
- package/package.json +121 -23
- package/tasty.config.ts +6 -0
- package/dist/_virtual/_rolldown/runtime.js +0 -7
- package/dist/chunks/cacheKey.js +0 -77
- package/dist/chunks/cacheKey.js.map +0 -1
- package/dist/chunks/definitions.d.ts +0 -37
- package/dist/chunks/definitions.js +0 -258
- package/dist/chunks/definitions.js.map +0 -1
- package/dist/chunks/index.d.ts +0 -1
- package/dist/chunks/renderChunk.d.ts +0 -1
- package/dist/chunks/renderChunk.js +0 -59
- package/dist/chunks/renderChunk.js.map +0 -1
- package/dist/compute-styles.d.ts +0 -31
- package/dist/compute-styles.js +0 -356
- package/dist/compute-styles.js.map +0 -1
- package/dist/config.d.ts +0 -372
- package/dist/config.js +0 -507
- package/dist/config.js.map +0 -1
- package/dist/counter-style/index.js +0 -51
- package/dist/counter-style/index.js.map +0 -1
- package/dist/debug.d.ts +0 -89
- package/dist/debug.js.map +0 -1
- package/dist/font-face/index.js +0 -63
- package/dist/font-face/index.js.map +0 -1
- package/dist/hooks/index.d.ts +0 -7
- package/dist/hooks/useCounterStyle.d.ts +0 -50
- package/dist/hooks/useCounterStyle.js +0 -45
- package/dist/hooks/useCounterStyle.js.map +0 -1
- package/dist/hooks/useFontFace.d.ts +0 -43
- package/dist/hooks/useFontFace.js +0 -69
- package/dist/hooks/useFontFace.js.map +0 -1
- package/dist/hooks/useGlobalStyles.d.ts +0 -30
- package/dist/hooks/useGlobalStyles.js +0 -77
- package/dist/hooks/useGlobalStyles.js.map +0 -1
- package/dist/hooks/useKeyframes.d.ts +0 -56
- package/dist/hooks/useKeyframes.js +0 -63
- package/dist/hooks/useKeyframes.js.map +0 -1
- package/dist/hooks/useProperty.d.ts +0 -79
- package/dist/hooks/useProperty.js +0 -108
- package/dist/hooks/useProperty.js.map +0 -1
- package/dist/hooks/useRawCSS.d.ts +0 -53
- package/dist/hooks/useRawCSS.js +0 -34
- package/dist/hooks/useRawCSS.js.map +0 -1
- package/dist/hooks/useStyles.d.ts +0 -40
- package/dist/hooks/useStyles.js +0 -29
- package/dist/hooks/useStyles.js.map +0 -1
- package/dist/injector/index.d.ts +0 -166
- package/dist/injector/index.js +0 -166
- package/dist/injector/index.js.map +0 -1
- package/dist/injector/injector.d.ts +0 -166
- package/dist/injector/injector.js +0 -464
- package/dist/injector/injector.js.map +0 -1
- package/dist/injector/sheet-manager.d.ts +0 -136
- package/dist/injector/sheet-manager.js +0 -733
- package/dist/injector/sheet-manager.js.map +0 -1
- package/dist/injector/types.d.ts +0 -204
- package/dist/keyframes/index.js +0 -206
- package/dist/keyframes/index.js.map +0 -1
- package/dist/parser/classify.js +0 -319
- package/dist/parser/classify.js.map +0 -1
- package/dist/parser/const.js +0 -60
- package/dist/parser/const.js.map +0 -1
- package/dist/parser/lru.js +0 -109
- package/dist/parser/lru.js.map +0 -1
- package/dist/parser/parser.d.ts +0 -25
- package/dist/parser/parser.js +0 -115
- package/dist/parser/parser.js.map +0 -1
- package/dist/parser/tokenizer.js +0 -69
- package/dist/parser/tokenizer.js.map +0 -1
- package/dist/parser/types.d.ts +0 -51
- package/dist/parser/types.js +0 -46
- package/dist/parser/types.js.map +0 -1
- package/dist/pipeline/conditions.d.ts +0 -134
- package/dist/pipeline/conditions.js +0 -406
- package/dist/pipeline/conditions.js.map +0 -1
- package/dist/pipeline/exclusive.js +0 -230
- package/dist/pipeline/exclusive.js.map +0 -1
- package/dist/pipeline/index.d.ts +0 -55
- package/dist/pipeline/index.js +0 -708
- package/dist/pipeline/index.js.map +0 -1
- package/dist/pipeline/materialize.js +0 -1103
- package/dist/pipeline/materialize.js.map +0 -1
- package/dist/pipeline/parseStateKey.d.ts +0 -15
- package/dist/pipeline/parseStateKey.js +0 -446
- package/dist/pipeline/parseStateKey.js.map +0 -1
- package/dist/pipeline/simplify.js +0 -515
- package/dist/pipeline/simplify.js.map +0 -1
- package/dist/pipeline/warnings.js +0 -18
- package/dist/pipeline/warnings.js.map +0 -1
- package/dist/plugins/index.d.ts +0 -2
- package/dist/plugins/okhsl-plugin.d.ts +0 -35
- package/dist/plugins/okhsl-plugin.js +0 -97
- package/dist/plugins/okhsl-plugin.js.map +0 -1
- package/dist/plugins/types.d.ts +0 -76
- package/dist/properties/index.js +0 -222
- package/dist/properties/index.js.map +0 -1
- package/dist/properties/property-type-resolver.d.ts +0 -24
- package/dist/properties/property-type-resolver.js +0 -90
- package/dist/properties/property-type-resolver.js.map +0 -1
- package/dist/ssr/async-storage.d.ts +0 -17
- package/dist/ssr/async-storage.js +0 -34
- package/dist/ssr/async-storage.js.map +0 -1
- package/dist/ssr/collect-auto-properties.js +0 -39
- package/dist/ssr/collect-auto-properties.js.map +0 -1
- package/dist/ssr/collector.d.ts +0 -102
- package/dist/ssr/collector.js +0 -219
- package/dist/ssr/collector.js.map +0 -1
- package/dist/ssr/format-global-rules.js +0 -22
- package/dist/ssr/format-global-rules.js.map +0 -1
- package/dist/ssr/format-keyframes.js +0 -69
- package/dist/ssr/format-keyframes.js.map +0 -1
- package/dist/ssr/format-property.js +0 -49
- package/dist/ssr/format-property.js.map +0 -1
- package/dist/ssr/format-rules.js +0 -73
- package/dist/ssr/format-rules.js.map +0 -1
- package/dist/ssr/hydrate.d.ts +0 -22
- package/dist/ssr/hydrate.js +0 -49
- package/dist/ssr/hydrate.js.map +0 -1
- package/dist/ssr/ssr-collector-ref.js +0 -12
- package/dist/ssr/ssr-collector-ref.js.map +0 -1
- package/dist/states/index.d.ts +0 -49
- package/dist/states/index.js +0 -170
- package/dist/states/index.js.map +0 -1
- package/dist/static/tastyStatic.d.ts +0 -46
- package/dist/static/tastyStatic.js +0 -30
- package/dist/static/tastyStatic.js.map +0 -1
- package/dist/static/types.d.ts +0 -49
- package/dist/static/types.js +0 -24
- package/dist/static/types.js.map +0 -1
- package/dist/styles/border.d.ts +0 -25
- package/dist/styles/border.js +0 -120
- package/dist/styles/border.js.map +0 -1
- package/dist/styles/color.d.ts +0 -14
- package/dist/styles/color.js +0 -26
- package/dist/styles/color.js.map +0 -1
- package/dist/styles/const.js +0 -17
- package/dist/styles/const.js.map +0 -1
- package/dist/styles/createStyle.js +0 -79
- package/dist/styles/createStyle.js.map +0 -1
- package/dist/styles/dimension.js +0 -109
- package/dist/styles/dimension.js.map +0 -1
- package/dist/styles/directional.js +0 -133
- package/dist/styles/directional.js.map +0 -1
- package/dist/styles/display.d.ts +0 -30
- package/dist/styles/display.js +0 -73
- package/dist/styles/display.js.map +0 -1
- package/dist/styles/fade.d.ts +0 -15
- package/dist/styles/fade.js +0 -62
- package/dist/styles/fade.js.map +0 -1
- package/dist/styles/fill.d.ts +0 -42
- package/dist/styles/fill.js +0 -51
- package/dist/styles/fill.js.map +0 -1
- package/dist/styles/flow.d.ts +0 -16
- package/dist/styles/flow.js +0 -12
- package/dist/styles/flow.js.map +0 -1
- package/dist/styles/gap.d.ts +0 -31
- package/dist/styles/gap.js +0 -38
- package/dist/styles/gap.js.map +0 -1
- package/dist/styles/height.d.ts +0 -17
- package/dist/styles/height.js +0 -19
- package/dist/styles/height.js.map +0 -1
- package/dist/styles/index.d.ts +0 -1
- package/dist/styles/index.js +0 -8
- package/dist/styles/index.js.map +0 -1
- package/dist/styles/inset.d.ts +0 -24
- package/dist/styles/inset.js +0 -34
- package/dist/styles/inset.js.map +0 -1
- package/dist/styles/list.d.ts +0 -16
- package/dist/styles/list.js +0 -100
- package/dist/styles/list.js.map +0 -1
- package/dist/styles/margin.d.ts +0 -24
- package/dist/styles/margin.js +0 -32
- package/dist/styles/margin.js.map +0 -1
- package/dist/styles/outline.d.ts +0 -29
- package/dist/styles/outline.js +0 -55
- package/dist/styles/outline.js.map +0 -1
- package/dist/styles/padding.d.ts +0 -24
- package/dist/styles/padding.js +0 -32
- package/dist/styles/padding.js.map +0 -1
- package/dist/styles/placement.d.ts +0 -37
- package/dist/styles/placement.js +0 -74
- package/dist/styles/placement.js.map +0 -1
- package/dist/styles/predefined.d.ts +0 -71
- package/dist/styles/predefined.js +0 -237
- package/dist/styles/predefined.js.map +0 -1
- package/dist/styles/preset.d.ts +0 -52
- package/dist/styles/preset.js +0 -127
- package/dist/styles/preset.js.map +0 -1
- package/dist/styles/radius.d.ts +0 -12
- package/dist/styles/radius.js +0 -83
- package/dist/styles/radius.js.map +0 -1
- package/dist/styles/scrollMargin.d.ts +0 -24
- package/dist/styles/scrollMargin.js +0 -32
- package/dist/styles/scrollMargin.js.map +0 -1
- package/dist/styles/scrollbar.d.ts +0 -25
- package/dist/styles/scrollbar.js +0 -51
- package/dist/styles/scrollbar.js.map +0 -1
- package/dist/styles/shadow.d.ts +0 -14
- package/dist/styles/shadow.js +0 -25
- package/dist/styles/shadow.js.map +0 -1
- package/dist/styles/shared.js +0 -17
- package/dist/styles/shared.js.map +0 -1
- package/dist/styles/transition.d.ts +0 -14
- package/dist/styles/transition.js +0 -159
- package/dist/styles/transition.js.map +0 -1
- package/dist/styles/types.d.ts +0 -564
- package/dist/styles/width.d.ts +0 -17
- package/dist/styles/width.js +0 -19
- package/dist/styles/width.js.map +0 -1
- package/dist/tasty.d.ts +0 -134
- package/dist/tasty.js +0 -240
- package/dist/tasty.js.map +0 -1
- package/dist/types.d.ts +0 -184
- package/dist/utils/cache-wrapper.js +0 -21
- package/dist/utils/cache-wrapper.js.map +0 -1
- package/dist/utils/case-converter.js +0 -8
- package/dist/utils/case-converter.js.map +0 -1
- package/dist/utils/color-math.d.ts +0 -46
- package/dist/utils/color-math.js +0 -749
- package/dist/utils/color-math.js.map +0 -1
- package/dist/utils/color-space.d.ts +0 -5
- package/dist/utils/color-space.js +0 -228
- package/dist/utils/color-space.js.map +0 -1
- package/dist/utils/colors.d.ts +0 -5
- package/dist/utils/colors.js +0 -10
- package/dist/utils/colors.js.map +0 -1
- package/dist/utils/css-types.d.ts +0 -7
- package/dist/utils/dotize.d.ts +0 -26
- package/dist/utils/dotize.js +0 -122
- package/dist/utils/dotize.js.map +0 -1
- package/dist/utils/filter-base-props.d.ts +0 -15
- package/dist/utils/filter-base-props.js +0 -45
- package/dist/utils/filter-base-props.js.map +0 -1
- package/dist/utils/get-display-name.d.ts +0 -7
- package/dist/utils/get-display-name.js +0 -10
- package/dist/utils/get-display-name.js.map +0 -1
- package/dist/utils/has-keys.js +0 -13
- package/dist/utils/has-keys.js.map +0 -1
- package/dist/utils/is-dev-env.js +0 -19
- package/dist/utils/is-dev-env.js.map +0 -1
- package/dist/utils/is-valid-element-type.js +0 -15
- package/dist/utils/is-valid-element-type.js.map +0 -1
- package/dist/utils/merge-styles.js.map +0 -1
- package/dist/utils/mod-attrs.d.ts +0 -6
- package/dist/utils/mod-attrs.js +0 -20
- package/dist/utils/mod-attrs.js.map +0 -1
- package/dist/utils/process-tokens.d.ts +0 -17
- package/dist/utils/process-tokens.js +0 -83
- package/dist/utils/process-tokens.js.map +0 -1
- package/dist/utils/resolve-recipes.d.ts +0 -17
- package/dist/utils/resolve-recipes.js +0 -146
- package/dist/utils/resolve-recipes.js.map +0 -1
- package/dist/utils/selector-transform.js +0 -32
- package/dist/utils/selector-transform.js.map +0 -1
- package/dist/utils/string.js +0 -8
- package/dist/utils/string.js.map +0 -1
- package/dist/utils/styles.d.ts +0 -99
- package/dist/utils/styles.js +0 -220
- package/dist/utils/styles.js.map +0 -1
- package/dist/utils/typography.d.ts +0 -47
- package/dist/utils/typography.js +0 -51
- package/dist/utils/typography.js.map +0 -1
- package/dist/utils/warnings.d.ts +0 -16
- package/dist/utils/warnings.js +0 -16
- package/dist/utils/warnings.js.map +0 -1
- package/dist/zero/css-writer.d.ts +0 -45
- package/dist/zero/css-writer.js +0 -73
- package/dist/zero/css-writer.js.map +0 -1
- package/dist/zero/extractor.d.ts +0 -24
- package/dist/zero/extractor.js.map +0 -1
- package/docs/PIPELINE.md +0 -519
- /package/dist/{chunks/cacheKey.d.ts → ssr/astro-client.d.ts} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"extractor.js","names":[],"sources":["../../src/zero/extractor.ts"],"sourcesContent":["import { createHash } from 'crypto';\n\nimport {\n categorizeStyleKeys,\n generateChunkCacheKey,\n renderStylesForChunk,\n} from '../chunks';\nimport type {\n CounterStyleDescriptors,\n FontFaceDescriptors,\n FontFaceInput,\n KeyframesSteps,\n} from '../injector/types';\nimport {\n extractLocalCounterStyle,\n formatCounterStyleRule,\n hasLocalCounterStyle,\n} from '../counter-style';\nimport {\n extractLocalFontFace,\n formatFontFaceRule,\n hasLocalFontFace,\n} from '../font-face';\nimport {\n extractAnimationNamesFromStyles,\n extractLocalKeyframes,\n filterUsedKeyframes,\n hasLocalKeyframes,\n mergeKeyframes,\n} from '../keyframes';\nimport type { StyleResult } from '../pipeline';\nimport { renderStyles } from '../pipeline';\nimport { extractLocalProperties, hasLocalProperties } from '../properties';\nimport { PropertyTypeResolver } from '../properties/property-type-resolver';\nimport type { Styles } from '../styles/types';\n\nexport interface ExtractedChunk {\n className: string;\n css: string;\n}\n\nexport interface ExtractedSelector {\n selector: string;\n css: string;\n}\n\nexport interface ExtractedKeyframes {\n name: string;\n css: string;\n}\n\nexport interface KeyframesExtractionResult {\n /** Keyframes to inject (deduplicated by content) */\n keyframes: ExtractedKeyframes[];\n /** Map from original animation name to canonical name (for replacement) */\n nameMap: Map<string, string>;\n}\n\n/**\n * Generate a deterministic className from a cache key using content hash.\n * This ensures the same styles always produce the same className,\n * regardless of build order or incremental compilation.\n */\nfunction generateClassName(cacheKey: string): string {\n const hash = createHash('md5').update(cacheKey).digest('hex').slice(0, 6);\n return `ts${hash}`; // 'ts' prefix for \"tasty-static\" to distinguish from runtime 't' classes\n}\n\n/**\n * Extract styles using chunking (for className mode).\n * Returns multiple classes, one per chunk.\n */\nexport function extractStylesWithChunks(styles: Styles): ExtractedChunk[] {\n const chunks: ExtractedChunk[] = [];\n\n // Categorize style keys into chunks\n const chunkMap = categorizeStyleKeys(styles as Record<string, unknown>);\n\n for (const [chunkName, chunkStyleKeys] of chunkMap) {\n if (chunkStyleKeys.length === 0) continue;\n\n // Generate cache key for this chunk (used for className hash)\n const cacheKey = generateChunkCacheKey(styles, chunkName, chunkStyleKeys);\n\n // Render styles for this chunk\n const renderResult = renderStylesForChunk(\n styles,\n chunkName,\n chunkStyleKeys,\n );\n\n if (renderResult.rules.length === 0) continue;\n\n // Generate deterministic className from content hash\n const className = generateClassName(cacheKey);\n const selector = `.${className}.${className}`;\n\n // Format CSS\n const css = formatRulesToCSS(renderResult.rules, selector);\n\n chunks.push({ className, css });\n }\n\n return chunks;\n}\n\n/**\n * Extract styles for a specific selector (for global/selector mode).\n * Returns a single CSS block.\n */\nexport function extractStylesForSelector(\n selector: string,\n styles: Styles,\n): ExtractedSelector {\n // renderStyles with selector returns StyleResult[] with selectors already applied\n const rules = renderStyles(styles, selector);\n // Format without re-prefixing - rules already have the full selector\n const css = formatRulesDirectly(rules);\n\n return { selector, css };\n}\n\n/**\n * Format StyleResult[] to CSS string.\n * Prefixes each rule's selector with the base selector.\n * Used for chunked styles where rules have relative selectors.\n */\nfunction formatRulesToCSS(rules: StyleResult[], baseSelector: string): string {\n return rules\n .map((rule) => {\n // Handle selector as array (OR conditions) or string\n // Note: renderStyles without className joins array selectors with '|||' placeholder\n const selectorParts = Array.isArray(rule.selector)\n ? rule.selector\n : rule.selector\n ? rule.selector.split('|||')\n : [''];\n\n // Prefix each selector part with the base selector\n const fullSelector = selectorParts\n .map((part) => {\n // Build selector: [rootPrefix] baseSelector[part]\n let selector: string;\n\n // If part is empty, just use base selector\n if (!part) {\n selector = baseSelector;\n } else if (part.startsWith(':') || part.startsWith('[')) {\n // If part starts with a pseudo-class or pseudo-element, append to base\n selector = `${baseSelector}${part}`;\n } else if (\n part.startsWith('>') ||\n part.startsWith('+') ||\n part.startsWith('~')\n ) {\n // If part starts with >, +, ~ combinator, append with space\n selector = `${baseSelector}${part}`;\n } else {\n // Otherwise, combine base with part\n selector = `${baseSelector}${part}`;\n }\n\n // Prepend rootPrefix if present (for @root() states)\n if (rule.rootPrefix) {\n selector = `${rule.rootPrefix} ${selector}`;\n }\n\n return selector;\n })\n .join(', ');\n\n let css = `${fullSelector} { ${rule.declarations} }`;\n\n // Wrap in at-rules (in reverse order for proper nesting)\n if (rule.atRules && rule.atRules.length > 0) {\n for (const atRule of [...rule.atRules].reverse()) {\n css = `${atRule} {\\n ${css}\\n}`;\n }\n }\n\n return css;\n })\n .join('\\n\\n');\n}\n\n/**\n * Format StyleResult[] to CSS string directly without prefixing.\n * Used for global styles where rules already have the full selector.\n */\nfunction formatRulesDirectly(rules: StyleResult[]): string {\n return rules\n .map((rule) => {\n // Prepend rootPrefix if present (for @root() states)\n const selector = rule.rootPrefix\n ? `${rule.rootPrefix} ${rule.selector}`\n : rule.selector;\n\n let css = `${selector} { ${rule.declarations} }`;\n\n // Wrap in at-rules (in reverse order for proper nesting)\n if (rule.atRules && rule.atRules.length > 0) {\n for (const atRule of [...rule.atRules].reverse()) {\n css = `${atRule} {\\n ${css}\\n}`;\n }\n }\n\n return css;\n })\n .join('\\n\\n');\n}\n\n// Note: With hash-based className generation, counter management functions\n// are no longer needed. ClassNames are deterministic based on content.\n\n/**\n * Generate a deterministic keyframes name from content hash.\n * This ensures the same keyframes content always produces the same name,\n * enabling automatic deduplication across elements and files.\n */\nfunction generateKeyframesName(steps: KeyframesSteps): string {\n const content = JSON.stringify(steps);\n const hash = createHash('md5').update(content).digest('hex').slice(0, 6);\n return `kf${hash}`; // 'kf' prefix for \"keyframes\"\n}\n\n/**\n * Extract keyframes that are used in styles.\n * Merges local @keyframes with global keyframes, filters to only used ones.\n * Generates hash-based names from content for automatic deduplication.\n *\n * @param styles - The styles object (may contain @keyframes and animation properties)\n * @param globalKeyframes - Optional global keyframes from config\n * @returns Keyframes to inject and name mapping for replacement\n */\nexport function extractKeyframesFromStyles(\n styles: Styles,\n globalKeyframes?: Record<string, KeyframesSteps> | null,\n): KeyframesExtractionResult {\n const emptyResult: KeyframesExtractionResult = {\n keyframes: [],\n nameMap: new Map(),\n };\n\n // Extract animation names from styles\n const usedNames = extractAnimationNamesFromStyles(styles);\n if (usedNames.size === 0) return emptyResult;\n\n // Merge local and global keyframes\n const local = hasLocalKeyframes(styles)\n ? extractLocalKeyframes(styles)\n : null;\n const allKeyframes = mergeKeyframes(local, globalKeyframes ?? null);\n\n // Filter to only used keyframes\n const usedKeyframes = filterUsedKeyframes(allKeyframes, usedNames);\n if (!usedKeyframes) return emptyResult;\n\n // Generate hash-based names and collect unique keyframes\n const seenHashes = new Set<string>();\n const nameMap = new Map<string, string>();\n const keyframesToEmit: ExtractedKeyframes[] = [];\n\n for (const [originalName, steps] of Object.entries(usedKeyframes)) {\n const hashedName = generateKeyframesName(steps);\n\n // Always map original name to hashed name (for CSS replacement)\n nameMap.set(originalName, hashedName);\n\n // Only emit each unique keyframe once\n if (!seenHashes.has(hashedName)) {\n seenHashes.add(hashedName);\n const css = keyframesToCSS(hashedName, steps);\n keyframesToEmit.push({ name: hashedName, css });\n }\n }\n\n return { keyframes: keyframesToEmit, nameMap };\n}\n\n/**\n * Convert keyframes steps to CSS string.\n */\nfunction keyframesToCSS(name: string, steps: KeyframesSteps): string {\n const stepRules: string[] = [];\n\n for (const [key, value] of Object.entries(steps)) {\n if (typeof value === 'string') {\n // Raw CSS string\n stepRules.push(`${key} { ${value.trim()} }`);\n } else if (value && typeof value === 'object') {\n // Style map - convert to CSS declarations\n const declarations = Object.entries(value)\n .map(([prop, val]) => {\n const cssProperty = camelToKebab(prop);\n return `${cssProperty}: ${val}`;\n })\n .join('; ');\n stepRules.push(`${key} { ${declarations} }`);\n }\n }\n\n return `@keyframes ${name} { ${stepRules.join(' ')} }`;\n}\n\n/**\n * Convert camelCase to kebab-case.\n */\nfunction camelToKebab(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);\n}\n\n// ============================================================================\n// Property Extraction (auto-infer @property types for zero-runtime)\n// ============================================================================\n\nexport interface ExtractedProperty {\n name: string;\n css: string;\n}\n\n/**\n * Extract auto-inferred @property declarations from styles.\n * Scans rendered style declarations and keyframe declarations for custom properties\n * whose types can be inferred from their values.\n *\n * @param styles - The styles object\n * @param options - Options including autoPropertyTypes flag\n * @returns Array of @property CSS rules to inject\n */\nexport function extractPropertiesFromStyles(\n styles: Styles,\n options?: { autoPropertyTypes?: boolean },\n): ExtractedProperty[] {\n if (options?.autoPropertyTypes === false) return [];\n\n const registered = new Set<string>();\n const results: ExtractedProperty[] = [];\n\n // Collect explicitly declared properties (they take precedence)\n if (hasLocalProperties(styles)) {\n const localProps = extractLocalProperties(styles);\n if (localProps) {\n for (const token of Object.keys(localProps)) {\n // Normalize token to CSS name\n let cssName: string;\n if (token.startsWith('#')) {\n cssName = `--${token.slice(1)}-color`;\n } else if (token.startsWith('$')) {\n cssName = `--${token.slice(1)}`;\n } else if (token.startsWith('--')) {\n cssName = token;\n } else {\n cssName = `--${token}`;\n }\n registered.add(cssName);\n }\n }\n }\n\n const resolver = new PropertyTypeResolver();\n\n const registerProperty = (\n name: string,\n syntax: string,\n initialValue: string,\n ) => {\n if (registered.has(name)) return;\n registered.add(name);\n\n const parts: string[] = [];\n parts.push(`syntax: \"${syntax}\";`);\n parts.push(`inherits: true;`);\n parts.push(`initial-value: ${initialValue};`);\n\n const css = `@property ${name} { ${parts.join(' ')} }`;\n results.push({ name, css });\n };\n\n const isPropertyDefined = (name: string) => registered.has(name);\n\n // Scan rendered style declarations\n const chunkMap = categorizeStyleKeys(styles as Record<string, unknown>);\n for (const [chunkName, chunkStyleKeys] of chunkMap) {\n if (chunkStyleKeys.length === 0) continue;\n const renderResult = renderStylesForChunk(\n styles,\n chunkName,\n chunkStyleKeys,\n );\n for (const rule of renderResult.rules) {\n if (!rule.declarations) continue;\n resolver.scanDeclarations(\n rule.declarations,\n isPropertyDefined,\n registerProperty,\n );\n }\n }\n\n // Scan keyframe declarations\n if (hasLocalKeyframes(styles)) {\n const localKf = extractLocalKeyframes(styles);\n if (localKf) {\n for (const steps of Object.values(localKf)) {\n scanKeyframeSteps(steps, resolver, isPropertyDefined, registerProperty);\n }\n }\n }\n\n return results;\n}\n\nfunction scanKeyframeSteps(\n steps: KeyframesSteps,\n resolver: PropertyTypeResolver,\n isPropertyDefined: (name: string) => boolean,\n registerProperty: (\n name: string,\n syntax: string,\n initialValue: string,\n ) => void,\n): void {\n for (const value of Object.values(steps)) {\n if (typeof value === 'string') {\n resolver.scanDeclarations(value, isPropertyDefined, registerProperty);\n } else if (value && typeof value === 'object') {\n const declarations = Object.entries(value)\n .map(([prop, val]) => {\n const cssProperty = camelToKebab(prop);\n return `${cssProperty}: ${val}`;\n })\n .join('; ');\n resolver.scanDeclarations(\n declarations,\n isPropertyDefined,\n registerProperty,\n );\n }\n }\n}\n\n// ============================================================================\n// Font Face Extraction (zero-runtime)\n// ============================================================================\n\nexport interface ExtractedFontFace {\n css: string;\n}\n\n/**\n * Extract @font-face rules from styles, merging with global config.\n * Deduplicates by content hash.\n */\nexport function extractFontFaceFromStyles(\n styles: Styles,\n globalFontFace?: Record<string, FontFaceInput> | null,\n): ExtractedFontFace[] {\n const results: ExtractedFontFace[] = [];\n const seenHashes = new Set<string>();\n\n function addFontFace(family: string, input: FontFaceInput) {\n const descriptors: FontFaceDescriptors[] = Array.isArray(input)\n ? input\n : [input];\n for (const desc of descriptors) {\n const hash = createHash('md5')\n .update(JSON.stringify({ family, ...desc }))\n .digest('hex')\n .slice(0, 8);\n if (!seenHashes.has(hash)) {\n seenHashes.add(hash);\n results.push({ css: formatFontFaceRule(family, desc) });\n }\n }\n }\n\n // Global font faces first\n if (globalFontFace) {\n for (const [family, input] of Object.entries(globalFontFace)) {\n addFontFace(family, input);\n }\n }\n\n // Local font faces (override globals with same hash)\n if (hasLocalFontFace(styles)) {\n const local = extractLocalFontFace(styles);\n if (local) {\n for (const [family, input] of Object.entries(local)) {\n addFontFace(family, input);\n }\n }\n }\n\n return results;\n}\n\n// ============================================================================\n// Counter Style Extraction (zero-runtime)\n// ============================================================================\n\nexport interface ExtractedCounterStyle {\n name: string;\n css: string;\n}\n\n/**\n * Extract @counter-style rules from styles, merging with global config.\n * Deduplicates by name (first definition wins).\n */\nexport function extractCounterStyleFromStyles(\n styles: Styles,\n globalCounterStyle?: Record<string, CounterStyleDescriptors> | null,\n): ExtractedCounterStyle[] {\n const results: ExtractedCounterStyle[] = [];\n const seenNames = new Set<string>();\n\n function addCounterStyle(name: string, descriptors: CounterStyleDescriptors) {\n if (!seenNames.has(name)) {\n seenNames.add(name);\n results.push({ name, css: formatCounterStyleRule(name, descriptors) });\n }\n }\n\n // Global counter styles first\n if (globalCounterStyle) {\n for (const [name, descriptors] of Object.entries(globalCounterStyle)) {\n addCounterStyle(name, descriptors);\n }\n }\n\n // Local counter styles (override globals with same name)\n if (hasLocalCounterStyle(styles)) {\n const local = extractLocalCounterStyle(styles);\n if (local) {\n for (const [name, descriptors] of Object.entries(local)) {\n addCounterStyle(name, descriptors);\n }\n }\n }\n\n return results;\n}\n"],"mappings":"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"}
|
package/docs/PIPELINE.md
DELETED
|
@@ -1,519 +0,0 @@
|
|
|
1
|
-
# Tasty Style Rendering Pipeline
|
|
2
|
-
|
|
3
|
-
This document describes the style rendering pipeline that transforms style objects into CSS rules. The pipeline ensures that each style value is applied to exactly one condition through exclusive condition building, boolean simplification, and intelligent CSS generation.
|
|
4
|
-
|
|
5
|
-
**Implementation:** [`src/pipeline/`](../src/pipeline/) — TypeScript file names below are relative to that directory.
|
|
6
|
-
|
|
7
|
-
## Overview
|
|
8
|
-
|
|
9
|
-
The pipeline takes a `Styles` object and produces an array of `CSSRule` objects ready for injection into the DOM. Entry points include `renderStylesPipeline` (full pipeline + optional class-name prefixing) and `renderStyles` (direct selector/class mode). The per-handler flow has seven main stages:
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
Input: Styles Object
|
|
13
|
-
↓
|
|
14
|
-
┌─────────────────────────────────────┐
|
|
15
|
-
│ 1. PARSE CONDITIONS │
|
|
16
|
-
│ parseStyleEntries + parseStateKey│
|
|
17
|
-
└─────────────────────────────────────┘
|
|
18
|
-
↓
|
|
19
|
-
┌─────────────────────────────────────┐
|
|
20
|
-
│ 2. BUILD EXCLUSIVE CONDITIONS │
|
|
21
|
-
│ Negate higher-priority entries │
|
|
22
|
-
└─────────────────────────────────────┘
|
|
23
|
-
↓
|
|
24
|
-
┌─────────────────────────────────────┐
|
|
25
|
-
│ 3. EXPAND AT-RULE OR BRANCHES │
|
|
26
|
-
│ expandExclusiveOrs (when needed)│
|
|
27
|
-
└─────────────────────────────────────┘
|
|
28
|
-
↓
|
|
29
|
-
┌─────────────────────────────────────┐
|
|
30
|
-
│ 4. COMPUTE STATE COMBINATIONS │
|
|
31
|
-
│ Cartesian product across styles │
|
|
32
|
-
└─────────────────────────────────────┘
|
|
33
|
-
↓
|
|
34
|
-
┌─────────────────────────────────────┐
|
|
35
|
-
│ 5. CALL HANDLERS │
|
|
36
|
-
│ Compute CSS declarations │
|
|
37
|
-
└─────────────────────────────────────┘
|
|
38
|
-
↓
|
|
39
|
-
┌─────────────────────────────────────┐
|
|
40
|
-
│ 6. MERGE BY VALUE │
|
|
41
|
-
│ Combine rules with same output │
|
|
42
|
-
└─────────────────────────────────────┘
|
|
43
|
-
↓
|
|
44
|
-
┌─────────────────────────────────────┐
|
|
45
|
-
│ 7. MATERIALIZE CSS │
|
|
46
|
-
│ Condition → selectors + at-rules│
|
|
47
|
-
└─────────────────────────────────────┘
|
|
48
|
-
↓
|
|
49
|
-
┌─────────────────────────────────────┐
|
|
50
|
-
│ runPipeline: dedupe identical rules │
|
|
51
|
-
└─────────────────────────────────────┘
|
|
52
|
-
↓
|
|
53
|
-
Output: CSSRule[]
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
**Simplification** (`simplifyCondition` in `simplify.ts`) is not a separate numbered stage. It runs inside exclusive building, `expandExclusiveOrs` branch cleanup, combination ANDs, merge-by-value ORs, and materialization as needed.
|
|
57
|
-
|
|
58
|
-
**Post-pass:** After `processStyles` collects rules from every handler, `runPipeline` filters duplicates using a key of `selector|declarations|atRules|rootPrefix` so identical emitted rules appear once.
|
|
59
|
-
|
|
60
|
-
---
|
|
61
|
-
|
|
62
|
-
## Stage 1: Parse Conditions
|
|
63
|
-
|
|
64
|
-
**Files:** `exclusive.ts` (`parseStyleEntries`), `parseStateKey.ts` (`parseStateKey`)
|
|
65
|
-
|
|
66
|
-
### What It Does
|
|
67
|
-
|
|
68
|
-
Converts each state key in a style value map (like `'hovered & !disabled'`, `'@media(w < 768px)'`) into `ConditionNode` trees. `parseStyleEntries` walks the object keys in source order and assigns priorities; `parseStateKey` parses a single key string.
|
|
69
|
-
|
|
70
|
-
### How It Works
|
|
71
|
-
|
|
72
|
-
1. **Tokenization**: The state key is split into tokens using a regex pattern that recognizes:
|
|
73
|
-
- Operators: `&` (AND), `|` (OR), `!` (NOT), `^` (XOR)
|
|
74
|
-
- Parentheses for grouping
|
|
75
|
-
- State tokens: `@media(...)`, `@root(...)`, `@parent(...)`, `@own(...)`, `@supports(...)`, `@(...)`, `@starting`, predefined states, modifiers, pseudo-classes
|
|
76
|
-
|
|
77
|
-
2. **Recursive Descent Parsing**: Tokens are parsed with operator precedence:
|
|
78
|
-
```
|
|
79
|
-
! (NOT) > ^ (XOR) > | (OR) > & (AND)
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
3. **State Token Interpretation**: Each state token is converted to a specific condition type:
|
|
83
|
-
- `hovered` → `ModifierCondition` with `attribute: 'data-hovered'`
|
|
84
|
-
- `theme=dark` → `ModifierCondition` with `attribute: 'data-theme', value: 'dark'`
|
|
85
|
-
- `:hover` → `PseudoCondition`
|
|
86
|
-
- `@media(w < 768px)` → `MediaCondition` (`subtype: 'dimension'`) with bounds
|
|
87
|
-
- `@media(prefers-color-scheme: dark)` → `MediaCondition` (`subtype: 'feature'`, `feature` + `featureValue`)
|
|
88
|
-
- `@root(schema=dark)` → `RootCondition` wrapping the inner condition
|
|
89
|
-
- `@parent(hovered)` → `ParentCondition` (optional `direct` for immediate parent)
|
|
90
|
-
- `@own(hovered)` → `OwnCondition` wrapping the parsed inner condition
|
|
91
|
-
- `@supports(display: grid)` → `SupportsCondition`
|
|
92
|
-
- `@(w < 600px)` → `ContainerCondition` (dimension, style, or raw subtypes)
|
|
93
|
-
- `@mobile` → Resolved via predefined states, then parsed recursively
|
|
94
|
-
|
|
95
|
-
Pipeline warnings for invalid inputs (e.g. bad `$` selector affix) are emitted from `warnings.ts`.
|
|
96
|
-
|
|
97
|
-
### Why
|
|
98
|
-
|
|
99
|
-
The condition tree representation enables:
|
|
100
|
-
- Boolean algebra operations (simplification, negation)
|
|
101
|
-
- Semantic analysis (detect contradictions)
|
|
102
|
-
- Flexible CSS generation (different output for media vs. selectors)
|
|
103
|
-
|
|
104
|
-
### Example
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
107
|
-
// Input
|
|
108
|
-
'hovered & @media(w < 768px)'
|
|
109
|
-
|
|
110
|
-
// Output ConditionNode
|
|
111
|
-
{
|
|
112
|
-
kind: 'compound',
|
|
113
|
-
operator: 'AND',
|
|
114
|
-
children: [
|
|
115
|
-
{ kind: 'state', type: 'modifier', attribute: 'data-hovered', ... },
|
|
116
|
-
{ kind: 'state', type: 'media', subtype: 'dimension', upperBound: { value: '768px', ... }, ... }
|
|
117
|
-
]
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
## Stage 2: Build Exclusive Conditions
|
|
124
|
-
|
|
125
|
-
**File:** `exclusive.ts` (`buildExclusiveConditions`)
|
|
126
|
-
|
|
127
|
-
### What It Does
|
|
128
|
-
|
|
129
|
-
Ensures each style entry applies in exactly one scenario by ANDing each condition with the negation of all higher-priority conditions.
|
|
130
|
-
|
|
131
|
-
### How It Works
|
|
132
|
-
|
|
133
|
-
Given entries ordered by priority (highest first):
|
|
134
|
-
```
|
|
135
|
-
A: value1 (priority 2)
|
|
136
|
-
B: value2 (priority 1)
|
|
137
|
-
C: value3 (priority 0)
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
Produces:
|
|
141
|
-
```
|
|
142
|
-
A: A (highest priority, no negation needed)
|
|
143
|
-
B: B & !A (applies only when A doesn't)
|
|
144
|
-
C: C & !A & !B (applies only when neither A nor B)
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
Each exclusive condition is passed through `simplifyCondition`. Entries that simplify to `FALSE` (impossible) are filtered out. The default state (`''` → `TrueCondition`) is not added to the “prior” list for negation (see `buildExclusiveConditions`).
|
|
148
|
-
|
|
149
|
-
### Why
|
|
150
|
-
|
|
151
|
-
This eliminates CSS specificity wars. Instead of relying on cascade order, each CSS rule matches in exactly one scenario. Benefits:
|
|
152
|
-
- Predictable styling regardless of rule order
|
|
153
|
-
- No conflicts from overlapping conditions
|
|
154
|
-
- Easier debugging (each rule is mutually exclusive)
|
|
155
|
-
|
|
156
|
-
### Example
|
|
157
|
-
|
|
158
|
-
```typescript
|
|
159
|
-
// Style value mapping
|
|
160
|
-
{ padding: { '': '2x', 'compact': '1x', '@media(w < 768px)': '0.5x' } }
|
|
161
|
-
|
|
162
|
-
// After exclusive building (highest priority first):
|
|
163
|
-
// @media(w < 768px): applies when media matches
|
|
164
|
-
// compact & !@media(w < 768px): applies when compact but NOT media
|
|
165
|
-
// !compact & !@media(w < 768px): default, applies when neither
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
---
|
|
169
|
-
|
|
170
|
-
## Stage 3: Expand At-Rule OR Branches
|
|
171
|
-
|
|
172
|
-
**File:** `exclusive.ts` (`expandExclusiveOrs`)
|
|
173
|
-
|
|
174
|
-
### What It Does
|
|
175
|
-
|
|
176
|
-
Runs **after** `buildExclusiveConditions`. When an entry’s **exclusive** condition contains a top-level OR that mixes **at-rule** context (`media`, `container`, `supports`, `starting`) with other branches, those ORs are split into mutually exclusive branches so each branch keeps the correct at-rule wrapping (e.g. after De Morgan: `!(A & B)` → `!A | !B`).
|
|
177
|
-
|
|
178
|
-
### How It Works
|
|
179
|
-
|
|
180
|
-
1. Collect top-level OR branches of `exclusiveCondition`.
|
|
181
|
-
2. If there is no OR, or **no** branch involves at-rule context, the entry is unchanged (pure selector ORs are handled later via `:is()` / variant merging in materialization).
|
|
182
|
-
3. Otherwise, branches are sorted with `sortOrBranchesForExpansion` so at-rule-heavy branches come first, then each branch is made exclusive against prior branches: `branch & !prior[0] & !prior[1] & ...`, then simplified.
|
|
183
|
-
4. Impossible branches are dropped; expanded entries get a synthetic `stateKey` suffix like `[or:0]`.
|
|
184
|
-
|
|
185
|
-
### Why
|
|
186
|
-
|
|
187
|
-
Without this pass, a condition like `!(@supports & :has)` could produce one rule missing the `@supports` wrapper. Exclusive OR expansion ensures negated at-rule groups still nest modifiers correctly.
|
|
188
|
-
|
|
189
|
-
### Example (conceptual)
|
|
190
|
-
|
|
191
|
-
See the comment block in `exclusive.ts` (~195–206): a default value’s exclusive condition can become `!@supports | !:has`; expansion yields one branch under `@supports (not …)` and another under `@supports (…) { :not(:has()) }` instead of a bare `:not(:has())` rule.
|
|
192
|
-
|
|
193
|
-
---
|
|
194
|
-
|
|
195
|
-
## Stage 4: Compute State Combinations
|
|
196
|
-
|
|
197
|
-
**File:** `index.ts` (`computeStateCombinations`)
|
|
198
|
-
|
|
199
|
-
### What It Does
|
|
200
|
-
|
|
201
|
-
Computes the Cartesian product of all style entries for a handler, creating snapshots of which value each style has for each possible state combination.
|
|
202
|
-
|
|
203
|
-
### How It Works
|
|
204
|
-
|
|
205
|
-
1. Collect exclusive entries for each style the handler uses
|
|
206
|
-
2. Compute Cartesian product: every combination of entries
|
|
207
|
-
3. For each combination:
|
|
208
|
-
- AND all `exclusiveCondition` values together
|
|
209
|
-
- `simplifyCondition` the result
|
|
210
|
-
- Skip if simplified to `FALSE`
|
|
211
|
-
- Record the values for each style
|
|
212
|
-
|
|
213
|
-
### Why
|
|
214
|
-
|
|
215
|
-
Style handlers often depend on multiple style properties (e.g., `padding` might look at both `padding` and `gap`). By computing all valid combinations, we can call the handler once per unique state and get the correct CSS output.
|
|
216
|
-
|
|
217
|
-
### Example
|
|
218
|
-
|
|
219
|
-
```typescript
|
|
220
|
-
// Handler looks up: ['padding', 'size']
|
|
221
|
-
// padding has entries: [{ value: '2x', condition: A }, { value: '1x', condition: B }]
|
|
222
|
-
// size has entries: [{ value: 'large', condition: C }, { value: 'small', condition: D }]
|
|
223
|
-
|
|
224
|
-
// Combinations:
|
|
225
|
-
// { padding: '2x', size: 'large', condition: A & C }
|
|
226
|
-
// { padding: '2x', size: 'small', condition: A & D }
|
|
227
|
-
// { padding: '1x', size: 'large', condition: B & C }
|
|
228
|
-
// { padding: '1x', size: 'small', condition: B & D }
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
---
|
|
232
|
-
|
|
233
|
-
## Stage 5: Call Handlers
|
|
234
|
-
|
|
235
|
-
**File:** `index.ts` (within `processStyles`)
|
|
236
|
-
|
|
237
|
-
### What It Does
|
|
238
|
-
|
|
239
|
-
Invokes style handlers with computed value snapshots to produce CSS declarations.
|
|
240
|
-
|
|
241
|
-
### How It Works
|
|
242
|
-
|
|
243
|
-
1. For each state snapshot (condition + values):
|
|
244
|
-
- Call the handler with the values
|
|
245
|
-
- Handler returns CSS properties (e.g., `{ 'padding-top': '16px', 'padding-bottom': '16px' }`)
|
|
246
|
-
- Handler may also return `$` (selector suffix) for pseudo-elements
|
|
247
|
-
2. Create computed rules with the condition, declarations, and selector suffix
|
|
248
|
-
|
|
249
|
-
### Why
|
|
250
|
-
|
|
251
|
-
Style handlers encapsulate the logic for translating design tokens (like `'2x'`) to actual CSS values (like `'16px'`). They can also handle complex multi-property styles (e.g., `padding` → `padding-top`, `padding-right`, etc.).
|
|
252
|
-
|
|
253
|
-
---
|
|
254
|
-
|
|
255
|
-
## Stage 6: Merge By Value
|
|
256
|
-
|
|
257
|
-
**File:** `index.ts` (`mergeByValue`)
|
|
258
|
-
|
|
259
|
-
### What It Does
|
|
260
|
-
|
|
261
|
-
Combines rules that have identical CSS output into a single rule with an OR condition.
|
|
262
|
-
|
|
263
|
-
### How It Works
|
|
264
|
-
|
|
265
|
-
1. Group rules by `selectorSuffix` plus a stable string for declarations (JSON via an internal `declStringCache` `WeakMap` on declaration objects)
|
|
266
|
-
2. For rules in the same group:
|
|
267
|
-
- Merge their conditions with OR
|
|
268
|
-
- `simplifyCondition` the resulting condition
|
|
269
|
-
3. Output one rule per group
|
|
270
|
-
|
|
271
|
-
### Why
|
|
272
|
-
|
|
273
|
-
Different state combinations might produce the same CSS output. Rather than emitting duplicate CSS, we combine them into a single rule. This reduces CSS size and improves performance.
|
|
274
|
-
|
|
275
|
-
### Example
|
|
276
|
-
|
|
277
|
-
```typescript
|
|
278
|
-
// Before merging:
|
|
279
|
-
// condition: A → { color: 'red' }
|
|
280
|
-
// condition: B → { color: 'red' }
|
|
281
|
-
|
|
282
|
-
// After merging:
|
|
283
|
-
// condition: A | B → { color: 'red' }
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
---
|
|
287
|
-
|
|
288
|
-
## Stage 7: Materialize CSS
|
|
289
|
-
|
|
290
|
-
**File:** `materialize.ts` (`conditionToCSS`, `materializeComputedRule` in `index.ts`)
|
|
291
|
-
|
|
292
|
-
### What It Does
|
|
293
|
-
|
|
294
|
-
Converts condition trees into actual CSS selectors and at-rules.
|
|
295
|
-
|
|
296
|
-
### How It Works
|
|
297
|
-
|
|
298
|
-
1. **Condition to CSS components** (`conditionToCSS`): Walk the condition tree and build `SelectorVariant` data:
|
|
299
|
-
- `ModifierCondition` → attribute selectors (e.g. `[data-hovered]`); optional `operator` (`=`, `^=`, `$=`, `*=`)
|
|
300
|
-
- `PseudoCondition` → pseudo-class (e.g. `:hover`)
|
|
301
|
-
- `MediaCondition` → `@media` (dimension, feature, or type)
|
|
302
|
-
- `ContainerCondition` → `@container` (dimension, style query, or raw)
|
|
303
|
-
- `RootCondition` → `rootGroups` / root prefix fragments
|
|
304
|
-
- `ParentCondition` → `parentGroups` / ancestor selectors (`direct` → child combinator path)
|
|
305
|
-
- `OwnCondition` → `ownGroups` on the **styled** element (sub-element / `&` scope), optimized with `optimizeGroups`
|
|
306
|
-
- `SupportsCondition` → `@supports` at-rules
|
|
307
|
-
- `StartingCondition` → `@starting-style` wrapper
|
|
308
|
-
|
|
309
|
-
2. **AND / OR on variants**: AND merges variant dimensions; OR yields multiple variants (later merged into `:is()` / `:not()` groups where appropriate).
|
|
310
|
-
|
|
311
|
-
3. **Contradiction detection**: During variant merging, impossible combinations are dropped (e.g. conflicting media, root, or modifier negations).
|
|
312
|
-
|
|
313
|
-
4. **`materializeComputedRule`**: Groups variants by sorted at-rules plus root-prefix key; within each group, `mergeVariantsIntoSelectorGroups` merges variants that differ only in flat modifier/pseudo parts; builds selector strings and emits one or more `CSSRule` objects.
|
|
314
|
-
|
|
315
|
-
### Why
|
|
316
|
-
|
|
317
|
-
CSS has different mechanisms for different condition types:
|
|
318
|
-
- Modifiers → attribute selectors
|
|
319
|
-
- Media queries → `@media` blocks
|
|
320
|
-
- Container queries → `@container` blocks
|
|
321
|
-
- Root state → `:root` / root groups
|
|
322
|
-
- Supports → `@supports` blocks
|
|
323
|
-
|
|
324
|
-
The materialization layer handles these differences while maintaining the logical semantics of the condition tree.
|
|
325
|
-
|
|
326
|
-
### Output Structure
|
|
327
|
-
|
|
328
|
-
```typescript
|
|
329
|
-
interface CSSRule {
|
|
330
|
-
selector: string | string[]; // Selector fragment(s); array when OR’d selector branches
|
|
331
|
-
declarations: string; // CSS declarations (e.g. 'color: red;')
|
|
332
|
-
atRules?: string[]; // Wrapping at-rules
|
|
333
|
-
rootPrefix?: string; // Root state prefix
|
|
334
|
-
}
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
When `renderStylesPipeline` runs **without** a class name, returned rules include `needsClassName: true` (compatibility field for the injector); that flag is not part of `CSSRule` inside `materialize.ts`.
|
|
338
|
-
|
|
339
|
-
---
|
|
340
|
-
|
|
341
|
-
## Condition Types
|
|
342
|
-
|
|
343
|
-
**File:** `conditions.ts`
|
|
344
|
-
|
|
345
|
-
### ConditionNode Hierarchy
|
|
346
|
-
|
|
347
|
-
```
|
|
348
|
-
ConditionNode
|
|
349
|
-
├── TrueCondition (matches everything)
|
|
350
|
-
├── FalseCondition (matches nothing)
|
|
351
|
-
├── CompoundCondition (AND/OR of children)
|
|
352
|
-
└── StateCondition
|
|
353
|
-
├── ModifierCondition (data attributes; optional value + match operator)
|
|
354
|
-
├── PseudoCondition (CSS pseudo-classes: :hover)
|
|
355
|
-
├── MediaCondition (subtype: dimension | feature | type)
|
|
356
|
-
├── ContainerCondition (subtype: dimension | style | raw)
|
|
357
|
-
├── RootCondition (inner condition under :root)
|
|
358
|
-
├── ParentCondition (@parent(...); optional direct parent)
|
|
359
|
-
├── OwnCondition (@own(...); scoped to styled / sub-element)
|
|
360
|
-
├── SupportsCondition (@supports(...))
|
|
361
|
-
└── StartingCondition (@starting-style wrapper)
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
### Key Operations
|
|
365
|
-
|
|
366
|
-
- `and(...conditions)`: Create AND with short-circuit and flattening
|
|
367
|
-
- `or(...conditions)`: Create OR with short-circuit and flattening
|
|
368
|
-
- `not(condition)`: Negate with De Morgan's law support
|
|
369
|
-
- `getConditionUniqueId(condition)`: Get canonical ID for comparison
|
|
370
|
-
|
|
371
|
-
---
|
|
372
|
-
|
|
373
|
-
## Simplification
|
|
374
|
-
|
|
375
|
-
**File:** `simplify.ts`
|
|
376
|
-
|
|
377
|
-
### What It Does
|
|
378
|
-
|
|
379
|
-
Applies boolean algebra rules to reduce condition complexity and detect impossible combinations.
|
|
380
|
-
|
|
381
|
-
### Rules Applied
|
|
382
|
-
|
|
383
|
-
1. **Identity Laws**:
|
|
384
|
-
- `A & TRUE = A`
|
|
385
|
-
- `A | FALSE = A`
|
|
386
|
-
|
|
387
|
-
2. **Annihilator Laws**:
|
|
388
|
-
- `A & FALSE = FALSE`
|
|
389
|
-
- `A | TRUE = TRUE`
|
|
390
|
-
|
|
391
|
-
3. **Contradiction Detection**:
|
|
392
|
-
- `A & !A = FALSE`
|
|
393
|
-
|
|
394
|
-
4. **Tautology Detection**:
|
|
395
|
-
- `A | !A = TRUE`
|
|
396
|
-
|
|
397
|
-
5. **Idempotent Laws** (via deduplication):
|
|
398
|
-
- `A & A = A`
|
|
399
|
-
- `A | A = A`
|
|
400
|
-
|
|
401
|
-
6. **Absorption Laws**:
|
|
402
|
-
- `A & (A | B) = A`
|
|
403
|
-
- `A | (A & B) = A`
|
|
404
|
-
|
|
405
|
-
7. **Range intersection**: For **media and container** dimension queries, impossible ranges simplify to `FALSE` (e.g. `@media(w > 400px) & @media(w < 300px)`).
|
|
406
|
-
|
|
407
|
-
8. **Container style queries**: Conflicting or redundant `@container` style conditions on the same property can be reduced (see `simplify.ts` around the container-style conflict pass).
|
|
408
|
-
|
|
409
|
-
9. **Attribute conflict detection**:
|
|
410
|
-
- `[data-theme="dark"] & [data-theme="light"] = FALSE`
|
|
411
|
-
|
|
412
|
-
### Why
|
|
413
|
-
|
|
414
|
-
Simplification reduces CSS output size and catches impossible combinations early, preventing invalid CSS rules from being generated.
|
|
415
|
-
|
|
416
|
-
---
|
|
417
|
-
|
|
418
|
-
## Caching Strategy
|
|
419
|
-
|
|
420
|
-
LRU and small auxiliary caches:
|
|
421
|
-
|
|
422
|
-
| Cache | Size | Key | Purpose |
|
|
423
|
-
|-------|------|-----|---------|
|
|
424
|
-
| `pipelineCache` | 5000 | `pipelineCacheKey \|\| stringifyStyles(styles)` | Skip full pipeline for identical styles |
|
|
425
|
-
| `parseCache` | 5000 | `trimmedStateKey + '\\0' + isSubElement + '\\0' + JSON.stringify(localPredefinedStates)` | Skip re-parsing identical state keys in context |
|
|
426
|
-
| `simplifyCache` | 5000 | `getConditionUniqueId(node)` | Skip re-simplifying identical conditions |
|
|
427
|
-
| `conditionCache` | 3000 | `getConditionUniqueId(node)` in `conditionToCSS` | Skip re-materializing identical conditions |
|
|
428
|
-
| `variantKeyCache` | — | `WeakMap<SelectorVariant, string>` | Stable string keys for variants during materialization |
|
|
429
|
-
| `declStringCache` | — | `WeakMap<Record<string,string>, string>` | Stable JSON keys for declaration objects in `mergeByValue` |
|
|
430
|
-
|
|
431
|
-
---
|
|
432
|
-
|
|
433
|
-
## Example Walkthrough
|
|
434
|
-
|
|
435
|
-
### Input
|
|
436
|
-
|
|
437
|
-
```typescript
|
|
438
|
-
const styles = {
|
|
439
|
-
color: {
|
|
440
|
-
'': '#white',
|
|
441
|
-
'@media(prefers-color-scheme: dark)': '#dark',
|
|
442
|
-
hovered: '#highlight',
|
|
443
|
-
},
|
|
444
|
-
};
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
### Stage 1: Parse Conditions
|
|
448
|
-
|
|
449
|
-
```
|
|
450
|
-
'' → TrueCondition
|
|
451
|
-
'@media(prefers-color-scheme: dark)' → MediaCondition(subtype: 'feature', feature: 'prefers-color-scheme', featureValue: 'dark')
|
|
452
|
-
'hovered' → ModifierCondition(attribute: 'data-hovered')
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
### Stages 2–3: Exclusive conditions + expand OR
|
|
456
|
-
|
|
457
|
-
Processing order (highest priority first): `hovered`, `@media(dark)`, default.
|
|
458
|
-
|
|
459
|
-
```
|
|
460
|
-
hovered: [data-hovered]
|
|
461
|
-
@media(dark) & !hovered: @media(dark) & :not([data-hovered])
|
|
462
|
-
!hovered & !@media(dark): :not([data-hovered]) & not @media(dark)
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
No at-rule OR expansion needed on these exclusives.
|
|
466
|
-
|
|
467
|
-
### Stages 4–5: Compute combinations and call handler
|
|
468
|
-
|
|
469
|
-
Single style, three snapshots; the `color` handler emits `color` plus `--current-color*` variables.
|
|
470
|
-
|
|
471
|
-
### Stage 6: Merge by value
|
|
472
|
-
|
|
473
|
-
Each snapshot yields distinct declarations; no merge.
|
|
474
|
-
|
|
475
|
-
### Stage 7: Materialize CSS
|
|
476
|
-
|
|
477
|
-
Using `renderStyles(styles, '.t1')` (single class prefix; `renderStylesPipeline` doubles the class for specificity when a class name is supplied):
|
|
478
|
-
|
|
479
|
-
```css
|
|
480
|
-
.t1[data-hovered] {
|
|
481
|
-
color: var(--highlight-color);
|
|
482
|
-
--current-color: var(--highlight-color);
|
|
483
|
-
--current-color-oklch: var(--highlight-color-oklch);
|
|
484
|
-
}
|
|
485
|
-
@media (prefers-color-scheme: dark) {
|
|
486
|
-
.t1:not([data-hovered]) {
|
|
487
|
-
color: var(--dark-color);
|
|
488
|
-
--current-color: var(--dark-color);
|
|
489
|
-
--current-color-oklch: var(--dark-color-oklch);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
@media (not (prefers-color-scheme: dark)) {
|
|
493
|
-
.t1:not([data-hovered]) {
|
|
494
|
-
color: var(--white-color);
|
|
495
|
-
--current-color: var(--white-color);
|
|
496
|
-
--current-color-oklch: var(--white-color-oklch);
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
---
|
|
502
|
-
|
|
503
|
-
## Key Design Decisions
|
|
504
|
-
|
|
505
|
-
### 1. Exclusive Conditions Over CSS Specificity
|
|
506
|
-
|
|
507
|
-
Rather than relying on CSS cascade rules, we generate mutually exclusive selectors. This makes styling predictable and debuggable.
|
|
508
|
-
|
|
509
|
-
### 2. OR Handling: DNF, `:is()`, and `expandExclusiveOrs`
|
|
510
|
-
|
|
511
|
-
OR of conditions is ultimately expressed as DNF (OR of ANDs) for CSS—comma-separated selectors, multiple rules, or `:is()` / `:not()` groups. **User-authored** ORs on pure selector conditions are handled in materialization. **`expandExclusiveOrs`** is an additional, **post-exclusive** pass for ORs that appear on **exclusive** conditions and involve **at-rule** branches (often from De Morgan on `@supports` / `@media` / `@container` / `@starting`), so each branch keeps correct at-rule nesting.
|
|
512
|
-
|
|
513
|
-
### 3. Early Contradiction Detection
|
|
514
|
-
|
|
515
|
-
Impossible combinations are detected at multiple levels (simplification, variant merging) to avoid generating invalid CSS.
|
|
516
|
-
|
|
517
|
-
### 4. Aggressive Caching
|
|
518
|
-
|
|
519
|
-
Parse, simplify, condition-to-CSS, and full-pipeline results are cached independently, enabling fast re-rendering when only parts of the style object change.
|
|
File without changes
|