From 675f49c33d4dc832a21d68aa3335ec1deb1c6d06 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Wed, 19 Feb 2025 16:57:34 +0700 Subject: [PATCH 1/3] refactor: generate presets with css cascade layers Closes https://github.com/webstudio-is/webstudio/issues/4796 CSS Cascade Layers are already widely support for almost 3 years. See https://developer.mozilla.org/en-US/docs/Web/CSS/@layer We can switch to it instead of wrapping selectors with `:where` all preset selectors. For older browsers impact will be insignificant as in most cases we use presets only to normalize browser styles. Generated code now looks like this ```css @layer presets { div.w-box { display: block; } } ``` --- .../app/__generated__/index.css | 22 +++---- .../app/__generated__/index.css | 22 +++---- .../app/__generated__/index.css | 22 +++---- .../app/__generated__/index.css | 16 ++--- fixtures/ssg/app/__generated__/index.css | 22 +++---- .../app/__generated__/index.css | 22 +++---- .../app/__generated__/index.css | 64 +++++++++---------- packages/sdk/src/css.test.tsx | 30 ++++----- packages/sdk/src/css.ts | 42 ++++++++---- 9 files changed, 138 insertions(+), 124 deletions(-) diff --git a/fixtures/react-router-docker/app/__generated__/index.css b/fixtures/react-router-docker/app/__generated__/index.css index dd85fdf13b99..2170b71f3d8f 100644 --- a/fixtures/react-router-docker/app/__generated__/index.css +++ b/fixtures/react-router-docker/app/__generated__/index.css @@ -1,4 +1,4 @@ -@media all { +@layer presets { :root { display: grid; min-height: 100%; @@ -8,7 +8,7 @@ white-space: pre-wrap; white-space-collapse: preserve; } - :where(body.w-body) { + body.w-body { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -18,7 +18,7 @@ -moz-osx-font-smoothing: grayscale; margin: 0; } - :where(h1.w-heading) { + h1.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -26,7 +26,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h2.w-heading) { + h2.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -34,7 +34,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h3.w-heading) { + h3.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -42,7 +42,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h4.w-heading) { + h4.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -50,7 +50,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h5.w-heading) { + h5.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -58,7 +58,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h6.w-heading) { + h6.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -66,7 +66,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(div.w-text) { + div.w-text { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -75,7 +75,7 @@ outline-width: 1px; min-height: 1em; } - :where(a.w-link) { + a.w-link { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -84,7 +84,7 @@ outline-width: 1px; display: inline-block; } - :where(img.w-image) { + img.w-image { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; diff --git a/fixtures/react-router-netlify/app/__generated__/index.css b/fixtures/react-router-netlify/app/__generated__/index.css index dd85fdf13b99..2170b71f3d8f 100644 --- a/fixtures/react-router-netlify/app/__generated__/index.css +++ b/fixtures/react-router-netlify/app/__generated__/index.css @@ -1,4 +1,4 @@ -@media all { +@layer presets { :root { display: grid; min-height: 100%; @@ -8,7 +8,7 @@ white-space: pre-wrap; white-space-collapse: preserve; } - :where(body.w-body) { + body.w-body { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -18,7 +18,7 @@ -moz-osx-font-smoothing: grayscale; margin: 0; } - :where(h1.w-heading) { + h1.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -26,7 +26,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h2.w-heading) { + h2.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -34,7 +34,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h3.w-heading) { + h3.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -42,7 +42,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h4.w-heading) { + h4.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -50,7 +50,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h5.w-heading) { + h5.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -58,7 +58,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h6.w-heading) { + h6.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -66,7 +66,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(div.w-text) { + div.w-text { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -75,7 +75,7 @@ outline-width: 1px; min-height: 1em; } - :where(a.w-link) { + a.w-link { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -84,7 +84,7 @@ outline-width: 1px; display: inline-block; } - :where(img.w-image) { + img.w-image { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; diff --git a/fixtures/react-router-vercel/app/__generated__/index.css b/fixtures/react-router-vercel/app/__generated__/index.css index dd85fdf13b99..2170b71f3d8f 100644 --- a/fixtures/react-router-vercel/app/__generated__/index.css +++ b/fixtures/react-router-vercel/app/__generated__/index.css @@ -1,4 +1,4 @@ -@media all { +@layer presets { :root { display: grid; min-height: 100%; @@ -8,7 +8,7 @@ white-space: pre-wrap; white-space-collapse: preserve; } - :where(body.w-body) { + body.w-body { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -18,7 +18,7 @@ -moz-osx-font-smoothing: grayscale; margin: 0; } - :where(h1.w-heading) { + h1.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -26,7 +26,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h2.w-heading) { + h2.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -34,7 +34,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h3.w-heading) { + h3.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -42,7 +42,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h4.w-heading) { + h4.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -50,7 +50,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h5.w-heading) { + h5.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -58,7 +58,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h6.w-heading) { + h6.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -66,7 +66,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(div.w-text) { + div.w-text { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -75,7 +75,7 @@ outline-width: 1px; min-height: 1em; } - :where(a.w-link) { + a.w-link { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -84,7 +84,7 @@ outline-width: 1px; display: inline-block; } - :where(img.w-image) { + img.w-image { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; diff --git a/fixtures/ssg-netlify-by-project-id/app/__generated__/index.css b/fixtures/ssg-netlify-by-project-id/app/__generated__/index.css index 3b9a2ccc741a..f94277d8c8e5 100644 --- a/fixtures/ssg-netlify-by-project-id/app/__generated__/index.css +++ b/fixtures/ssg-netlify-by-project-id/app/__generated__/index.css @@ -1,4 +1,4 @@ -@media all { +@layer presets { :root { display: grid; min-height: 100%; @@ -8,7 +8,7 @@ white-space: pre-wrap; white-space-collapse: preserve; } - :where(body.w-body) { + body.w-body { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -18,7 +18,7 @@ -moz-osx-font-smoothing: grayscale; margin: 0; } - :where(h1.w-heading) { + h1.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -26,7 +26,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h2.w-heading) { + h2.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -34,7 +34,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h3.w-heading) { + h3.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -42,7 +42,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h4.w-heading) { + h4.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -50,7 +50,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h5.w-heading) { + h5.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -58,7 +58,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h6.w-heading) { + h6.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; diff --git a/fixtures/ssg/app/__generated__/index.css b/fixtures/ssg/app/__generated__/index.css index dd85fdf13b99..2170b71f3d8f 100644 --- a/fixtures/ssg/app/__generated__/index.css +++ b/fixtures/ssg/app/__generated__/index.css @@ -1,4 +1,4 @@ -@media all { +@layer presets { :root { display: grid; min-height: 100%; @@ -8,7 +8,7 @@ white-space: pre-wrap; white-space-collapse: preserve; } - :where(body.w-body) { + body.w-body { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -18,7 +18,7 @@ -moz-osx-font-smoothing: grayscale; margin: 0; } - :where(h1.w-heading) { + h1.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -26,7 +26,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h2.w-heading) { + h2.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -34,7 +34,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h3.w-heading) { + h3.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -42,7 +42,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h4.w-heading) { + h4.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -50,7 +50,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h5.w-heading) { + h5.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -58,7 +58,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h6.w-heading) { + h6.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -66,7 +66,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(div.w-text) { + div.w-text { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -75,7 +75,7 @@ outline-width: 1px; min-height: 1em; } - :where(a.w-link) { + a.w-link { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -84,7 +84,7 @@ outline-width: 1px; display: inline-block; } - :where(img.w-image) { + img.w-image { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; diff --git a/fixtures/webstudio-cloudflare-template/app/__generated__/index.css b/fixtures/webstudio-cloudflare-template/app/__generated__/index.css index dd85fdf13b99..2170b71f3d8f 100644 --- a/fixtures/webstudio-cloudflare-template/app/__generated__/index.css +++ b/fixtures/webstudio-cloudflare-template/app/__generated__/index.css @@ -1,4 +1,4 @@ -@media all { +@layer presets { :root { display: grid; min-height: 100%; @@ -8,7 +8,7 @@ white-space: pre-wrap; white-space-collapse: preserve; } - :where(body.w-body) { + body.w-body { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -18,7 +18,7 @@ -moz-osx-font-smoothing: grayscale; margin: 0; } - :where(h1.w-heading) { + h1.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -26,7 +26,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h2.w-heading) { + h2.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -34,7 +34,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h3.w-heading) { + h3.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -42,7 +42,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h4.w-heading) { + h4.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -50,7 +50,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h5.w-heading) { + h5.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -58,7 +58,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h6.w-heading) { + h6.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -66,7 +66,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(div.w-text) { + div.w-text { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -75,7 +75,7 @@ outline-width: 1px; min-height: 1em; } - :where(a.w-link) { + a.w-link { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -84,7 +84,7 @@ outline-width: 1px; display: inline-block; } - :where(img.w-image) { + img.w-image { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; diff --git a/fixtures/webstudio-features/app/__generated__/index.css b/fixtures/webstudio-features/app/__generated__/index.css index 844c0255bde4..64c7211b72ea 100644 --- a/fixtures/webstudio-features/app/__generated__/index.css +++ b/fixtures/webstudio-features/app/__generated__/index.css @@ -1,4 +1,4 @@ -@media all { +@layer presets { :root { display: grid; min-height: 100%; @@ -8,7 +8,7 @@ white-space: pre-wrap; white-space-collapse: preserve; } - :where(body.w-body) { + body.w-body { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -18,7 +18,7 @@ -moz-osx-font-smoothing: grayscale; margin: 0; } - :where(h1.w-heading) { + h1.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -26,7 +26,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h2.w-heading) { + h2.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -34,7 +34,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h3.w-heading) { + h3.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -42,7 +42,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h4.w-heading) { + h4.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -50,7 +50,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h5.w-heading) { + h5.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -58,7 +58,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h6.w-heading) { + h6.w-heading { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -66,7 +66,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(div.w-box) { + div.w-box { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -74,7 +74,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(address.w-box) { + address.w-box { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -82,7 +82,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(article.w-box) { + article.w-box { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -90,7 +90,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(aside.w-box) { + aside.w-box { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -98,7 +98,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(figure.w-box) { + figure.w-box { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -106,7 +106,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(footer.w-box) { + footer.w-box { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -114,7 +114,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(header.w-box) { + header.w-box { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -122,7 +122,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(main.w-box) { + main.w-box { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -130,7 +130,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(nav.w-box) { + nav.w-box { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -138,7 +138,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(section.w-box) { + section.w-box { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -146,7 +146,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(p.w-paragraph) { + p.w-paragraph { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -154,7 +154,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(img.w-image) { + img.w-image { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -165,7 +165,7 @@ display: block; height: auto; } - :where(a.w-link) { + a.w-link { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -174,7 +174,7 @@ outline-width: 1px; display: inline-block; } - :where(div.w-text) { + div.w-text { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -183,7 +183,7 @@ outline-width: 1px; min-height: 1em; } - :where(div.w-accordion) { + div.w-accordion { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -191,7 +191,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(div.w-item) { + div.w-item { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -199,7 +199,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(h3.w-item-header) { + h3.w-item-header { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -209,7 +209,7 @@ margin-top: 0px; margin-bottom: 0px; } - :where(button.w-item-trigger) { + button.w-item-trigger { font-family: inherit; font-size: 100%; line-height: 1.15; @@ -221,12 +221,12 @@ margin: 0; padding: 0px; } - :where(div.w-html-embed) { + div.w-html-embed { display: contents; white-space: normal; white-space-collapse: collapse; } - :where(div.w-item-content) { + div.w-item-content { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -234,7 +234,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(form.w-webhook-form) { + form.w-webhook-form { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -242,7 +242,7 @@ border-left-width: 1px; outline-width: 1px; } - :where(label.w-input-label) { + label.w-input-label { box-sizing: border-box; border-top-width: 1px; border-right-width: 1px; @@ -251,7 +251,7 @@ outline-width: 1px; display: block; } - :where(input.w-text-input) { + input.w-text-input { font-family: inherit; font-size: 100%; line-height: 1.15; @@ -267,7 +267,7 @@ display: block; margin: 0; } - :where(button.w-button) { + button.w-button { font-family: inherit; font-size: 100%; line-height: 1.15; diff --git a/packages/sdk/src/css.test.tsx b/packages/sdk/src/css.test.tsx index b43efe6a5d62..ce81a469cb15 100644 --- a/packages/sdk/src/css.test.tsx +++ b/packages/sdk/src/css.test.tsx @@ -182,11 +182,11 @@ test("generate component presets with multiple tags", () => { assetBaseUrl: "", }); expect(cssText).toMatchInlineSnapshot(` -"@media all { - :where(div.w-list-item) { +"@layer presets { + div.w-list-item { display: block } - :where(a.w-list-item) { + a.w-list-item { -webkit-user-select: none; user-select: none } @@ -256,14 +256,14 @@ test("deduplicate component presets for similarly named components", () => { assetBaseUrl: "", }); expect(cssText).toMatchInlineSnapshot(` -"@media all { - :where(div.w-list-item) { +"@layer presets { + div.w-list-item { display: block } - :where(div.w-list-item-1) { + div.w-list-item-1 { display: flex } - :where(div.w-list-item-2) { + div.w-list-item-2 { display: grid } } @@ -326,11 +326,11 @@ test("expose preset classes to instances", () => { assetBaseUrl: "", }); expect(atomicCssText).toMatchInlineSnapshot(` -"@media all { - :where(div.w-body) { +"@layer presets { + div.w-body { display: block } - :where(div.w-box) { + div.w-box { display: flex } } @@ -424,11 +424,11 @@ test("generate classes with instance and meta label", () => { assetBaseUrl: "", }); expect(cssText).toMatchInlineSnapshot(` -"@media all { - :where(div.w-body-meta-label) { +"@layer presets { + div.w-body-meta-label { display: block } - :where(div.w-box-meta-label) { + div.w-box-meta-label { display: flex } } @@ -507,7 +507,7 @@ test("generate :root preset and user styles", () => { assetBaseUrl: "", }); expect(cssText).toMatchInlineSnapshot(` -"@media all { +"@layer presets { :root { display: grid } @@ -521,7 +521,7 @@ test("generate :root preset and user styles", () => { `); expect(classes).toEqual(new Map()); expect(atomicCssText).toMatchInlineSnapshot(` -"@media all { +"@layer presets { :root { display: grid } diff --git a/packages/sdk/src/css.ts b/packages/sdk/src/css.ts index 2e7f71d579e5..090df2fd87fa 100644 --- a/packages/sdk/src/css.ts +++ b/packages/sdk/src/css.ts @@ -90,11 +90,12 @@ export const generateCss = ({ assetBaseUrl, atomic, }: CssConfig) => { - const globalSheet = createRegularStyleSheet({ name: "ssr" }); - const sheet = createRegularStyleSheet({ name: "ssr" }); + const fontSheet = createRegularStyleSheet({ name: "ssr" }); + const presetSheet = createRegularStyleSheet({ name: "ssr" }); + const userSheet = createRegularStyleSheet({ name: "ssr" }); - addFontRules({ sheet: globalSheet, assets, assetBaseUrl }); - globalSheet.addMediaRule("presets"); + addFontRules({ sheet: fontSheet, assets, assetBaseUrl }); + presetSheet.addMediaRule("presets"); const presetClasses = new Map(); const scope = createScope([], normalizeClassName, "-"); for (const [component, meta] of componentMetas) { @@ -105,14 +106,15 @@ export const generateCss = ({ // add preset class only when at least one style is defined presetClasses.set(component, className); } + // @todo reset specificity with css cascade layers instead of :where for (const [tag, styles] of presetStyle) { // use :where() to reset specificity of preset selector // and let user styles completely override it // ideally switch to @layer when better supported // render root preset styles without changes const selector = - component === rootComponent ? ":root" : `:where(${tag}.${className})`; - const rule = globalSheet.addNestingRule(selector); + component === rootComponent ? ":root" : `${tag}.${className}`; + const rule = presetSheet.addNestingRule(selector); for (const declaration of styles) { rule.setDeclaration({ breakpoint: "presets", @@ -125,16 +127,16 @@ export const generateCss = ({ } for (const breakpoint of breakpoints.values()) { - sheet.addMediaRule(breakpoint.id, breakpoint); + userSheet.addMediaRule(breakpoint.id, breakpoint); } const imageValueTransformer = createImageValueTransformer(assets, { assetBaseUrl, }); - sheet.setTransformer(imageValueTransformer); + userSheet.setTransformer(imageValueTransformer); for (const styleDecl of styles.values()) { - const rule = sheet.addMixinRule(styleDecl.styleSourceId); + const rule = userSheet.addMixinRule(styleDecl.styleSourceId); rule.setDeclaration({ breakpoint: styleDecl.breakpointId, selector: styleDecl.state ?? "", @@ -170,7 +172,7 @@ export const generateCss = ({ const { values } = selection; // special case for :root styles if (instanceId === ROOT_INSTANCE_ID) { - const rule = sheet.addNestingRule(`:root`); + const rule = userSheet.addNestingRule(`:root`); rule.applyMixins(values); // avoid storing in instanceByRule to prevent conversion into atomic styles continue; @@ -201,21 +203,33 @@ export const generateCss = ({ } classList.push(className); } - const rule = sheet.addNestingRule(`.${className}`, descendantSuffix); + const rule = userSheet.addNestingRule(`.${className}`, descendantSuffix); rule.applyMixins(values); instanceByRule.set(rule, instanceId); } + const fontCss = fontSheet.cssText; + // render presets inside of cascade layer to let user completely override all properties + // user agent (browser) styles work in the same way + // for example a { color: black } overrides a:visited as well + const presetCss = presetSheet.cssText.replaceAll( + "@media all ", + "@layer presets " + ); + if (atomic) { - const { cssText } = generateAtomic(sheet, { + const { cssText } = generateAtomic(userSheet, { getKey: (rule) => instanceByRule.get(rule), transformValue: imageValueTransformer, classes, }); - return { cssText: `${globalSheet.cssText}\n${cssText}`, classes }; + return { + cssText: `${fontCss}${presetCss}\n${cssText}`, + classes, + }; } return { - cssText: `${globalSheet.cssText}\n${sheet.cssText}`, + cssText: `${fontCss}${presetCss}\n${userSheet.cssText}`, classes, }; }; From 909ab6a0ade5fc8ab0ee9448601bffe675ba0def Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Wed, 19 Feb 2025 17:15:16 +0700 Subject: [PATCH 2/3] Trigger rebuild From 030779ec38ed9ab6b4caa7c74691bd3ab7eac101 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Thu, 20 Feb 2025 01:41:20 +0700 Subject: [PATCH 3/3] Add todo --- packages/sdk/src/css.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/sdk/src/css.ts b/packages/sdk/src/css.ts index 090df2fd87fa..44e164c526db 100644 --- a/packages/sdk/src/css.ts +++ b/packages/sdk/src/css.ts @@ -212,6 +212,7 @@ export const generateCss = ({ // render presets inside of cascade layer to let user completely override all properties // user agent (browser) styles work in the same way // for example a { color: black } overrides a:visited as well + // @todo figure out proper API to work with layers when more use cases are known const presetCss = presetSheet.cssText.replaceAll( "@media all ", "@layer presets "