From 34577ccc0c70c24f3a631f16c5df95c6638211af Mon Sep 17 00:00:00 2001 From: "Timo K." Date: Thu, 1 Oct 2026 13:54:26 +0200 Subject: [PATCH] remove scopeStylesToRoot --- README.md | 9 +- component/build/scopeStylesToRoot.test.ts | 112 ------- docs/agents/code-style.md | 5 +- knip.ts | 2 - package.json | 2 - pnpm-lock.yaml | 11 - src/base.css | 83 +++-- src/base.css.test.ts | 42 +++ src/index.css | 7 + src/normalize.css | 354 ++++++++++++++++++++++ vite-component-dev.config.ts | 4 - vite-component.config.ts | 4 - 12 files changed, 450 insertions(+), 185 deletions(-) delete mode 100644 component/build/scopeStylesToRoot.test.ts create mode 100644 src/base.css.test.ts create mode 100644 src/normalize.css diff --git a/README.md b/README.md index e7f9a62d7..9844398bb 100644 --- a/README.md +++ b/README.md @@ -252,11 +252,10 @@ for the standalone app the root is the page, so they mean what the media queries they replaced did. (The standalone-only views, such as the home and login pages, still use plain media queries, since the component never shows them.) -The component's stylesheet is confined to the element it is mounted in: the -build rewrites every selector so that it matches only Element Call's root or -what is inside it, with `html`, `body` and `:root` standing for that root (see -`component/build/scopeStylesToRoot.ts`). A host's own page keeps its styles, -and Element Call brings its own fonts and design tokens along. +The component's stylesheet is confined to the element it is mounted in: its +base styles — element defaults and custom properties — are written against +Element Call's root element rather than the document, and everything else is a +CSS module. A host's own page keeps its styles. The component speaks every language the app does. English is bundled in; the other locales are split into chunks the host's bundler loads the first time diff --git a/component/build/scopeStylesToRoot.test.ts b/component/build/scopeStylesToRoot.test.ts deleted file mode 100644 index ae42062c3..000000000 --- a/component/build/scopeStylesToRoot.test.ts +++ /dev/null @@ -1,112 +0,0 @@ -/* -Copyright 2026 Element Creations Ltd. - -SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE in the repository root for full details. -*/ - -import { describe, expect, it } from "vitest"; -import postcss from "postcss"; - -import { ROOT_SELECTOR, scopeStylesToRoot } from "./scopeStylesToRoot"; - -const inRoot = `:where(${ROOT_SELECTOR}, ${ROOT_SELECTOR} *)`; -const isRoot = `:where(${ROOT_SELECTOR})`; - -async function scope(css: string, file = "base.css"): Promise { - const result = await postcss([scopeStylesToRoot()]).process(css, { - from: file, - }); - return result.css; -} - -describe("scopeStylesToRoot", () => { - it("makes the root stand in for the document", async () => { - expect(await scope("html { line-height: 1.15 }")).toBe( - `${isRoot} { line-height: 1.15 }`, - ); - expect(await scope("body { margin: 0 }")).toBe(`${isRoot} { margin: 0 }`); - expect(await scope(":root { --a: 1 }")).toBe(`${isRoot} { --a: 1 }`); - expect(await scope("body.no-scroll-body { position: fixed }")).toBe( - `${isRoot}.no-scroll-body { position: fixed }`, - ); - expect(await scope("body .x { color: red }")).toBe( - `${isRoot} .x { color: red }`, - ); - }); - - it("collapses selectors that all became the root", async () => { - expect(await scope("html, body, input { font: inherit }")).toBe( - `${isRoot},input${inRoot} { font: inherit }`, - ); - }); - - it("confines everything else to the root and what is inside it", async () => { - expect(await scope("h1 { margin: 0 }")).toBe(`h1${inRoot} { margin: 0 }`); - expect(await scope(".cpd-theme-dark { --a: 1 }")).toBe( - `.cpd-theme-dark${inRoot} { --a: 1 }`, - ); - expect(await scope("* { box-sizing: border-box }")).toBe( - `*${inRoot} { box-sizing: border-box }`, - ); - expect(await scope(".a > .b + .c { color: red }")).toBe( - `.a>.b+.c${inRoot} { color: red }`, - ); - }); - - it("keeps pseudo-elements last", async () => { - expect(await scope("button::-moz-focus-inner { border: 0 }")).toBe( - `button${inRoot}::-moz-focus-inner { border: 0 }`, - ); - expect(await scope(".a .b:hover::after { content: '' }")).toBe( - `.a .b:hover${inRoot}::after { content: '' }`, - ); - expect(await scope("p:first-letter { color: red }")).toBe( - `p${inRoot}:first-letter { color: red }`, - ); - }); - - it("leaves alone what already names the root", async () => { - const css = `${ROOT_SELECTOR}[data-platform="ios"] { --a: 1 }`; - expect(await scope(css)).toBe(css); - }); - - it("reaches into layers and media queries", async () => { - expect( - await scope( - "@layer normalize { h1 { margin: 0 } } @media (min-width: 1px) { p { margin: 0 } }", - ), - ).toBe( - `@layer normalize { h1${inRoot} { margin: 0 } } @media (min-width: 1px) { p${inRoot} { margin: 0 } }`, - ); - }); - - it("does not touch keyframes or nested rules", async () => { - expect( - await scope("@keyframes spin { from { opacity: 0 } to { opacity: 1 } }"), - ).toBe("@keyframes spin { from { opacity: 0 } to { opacity: 1 } }"); - expect( - await scope( - ".a { color: red; &:hover { color: blue } .b { color: green } }", - ), - ).toBe( - `.a${inRoot} { color: red; &:hover { color: blue } .b { color: green } }`, - ); - }); - - it("only touches the bare selectors of a CSS module", async () => { - const file = "Settings.module.css"; - expect(await scope("pre { font-size: 1px }", file)).toBe( - `pre${inRoot} { font-size: 1px }`, - ); - expect(await scope(".modal pre { font-size: 1px }", file)).toBe( - `.modal pre${inRoot} { font-size: 1px }`, - ); - expect(await scope(".box_abc12 { border: 0 }", file)).toBe( - ".box_abc12 { border: 0 }", - ); - expect(await scope(".a .b_abc12:hover { border: 0 }", file)).toBe( - ".a .b_abc12:hover { border: 0 }", - ); - }); -}); diff --git a/docs/agents/code-style.md b/docs/agents/code-style.md index 31bf488ac..4fed5873c 100644 --- a/docs/agents/code-style.md +++ b/docs/agents/code-style.md @@ -82,8 +82,9 @@ its caller? If not, cut it. - Size against the root, not the window: `@container element-call (…)` and `cqw` / `cqh`, never `@media (width)` or `vw` / `vh`. Media queries stay correct only in standalone-only views — home, login. -- Style `[data-element-call-root]`, never `body` or `:root`; the component build - makes those stand for the root (`component/build/scopeStylesToRoot.ts`). +- Style `[data-element-call-root]`, never `body` or `:root`. A rule that matches + by element alone takes the root as a prefix (see `src/base.css`); nothing + rewrites selectors at build time, so a bare one reaches the host's page. ## Strings and a11y diff --git a/knip.ts b/knip.ts index d9de80c8c..fa88f97f9 100644 --- a/knip.ts +++ b/knip.ts @@ -33,8 +33,6 @@ export default { "src/state/ServiceInterruptionsViewModel.ts", ], ignoreDependencies: [ - // Used in CSS - "normalize.css", // Used for its global type declarations "@types/grecaptcha", "@types/sdp-transform", diff --git a/package.json b/package.json index c7642f57a..8324fdfac 100644 --- a/package.json +++ b/package.json @@ -106,14 +106,12 @@ "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", "matrix-widget-api": "^1.18.0", "node-stdlib-browser": "^1.3.1", - "normalize.css": "^8.0.1", "oxfmt": "^0.66.0", "oxlint": "^1.70.0", "oxlint-tsgolint": "^0.25.0", "pako": "^2.0.4", "postcss": "^8.4.41", "postcss-preset-env": "^10.0.0", - "postcss-selector-parser": "^7.1.1", "posthog-js": "1.408.2", "qrcode": "^1.5.4", "react": "19", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be07cd290..a73182463 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -199,9 +199,6 @@ importers: node-stdlib-browser: specifier: ^1.3.1 version: 1.3.1 - normalize.css: - specifier: ^8.0.1 - version: 8.0.1 oxfmt: specifier: ^0.66.0 version: 0.66.0 @@ -220,9 +217,6 @@ importers: postcss-preset-env: specifier: ^10.0.0 version: 10.6.1(postcss@8.5.26) - postcss-selector-parser: - specifier: ^7.1.1 - version: 7.1.3 posthog-js: specifier: 1.408.2 version: 1.408.2 @@ -4784,9 +4778,6 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - normalize.css@8.0.1: - resolution: {integrity: sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==} - npm-run-path@6.0.0: resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} engines: {node: '>=18'} @@ -10369,8 +10360,6 @@ snapshots: normalize-path@3.0.0: {} - normalize.css@8.0.1: {} - npm-run-path@6.0.0: dependencies: path-key: 4.0.0 diff --git a/src/base.css b/src/base.css index 51751eb79..65c667df1 100644 --- a/src/base.css +++ b/src/base.css @@ -10,14 +10,22 @@ and element defaults its own stylesheets build on top of. Split out from index.css so that Element Call as a component can have these without also being given the standalone page's layout, which would style the -host's own document. What remains here still speaks of the -document — normalize.css and the typography below use bare element selectors, -and the custom properties are declared on `:root` — which is right for the -page, and is why the component build rewrites it: there every selector is -confined to Element Call's root element (see component/build/scopeStylesToRoot.ts), -with `html`, `body` and `:root` becoming that element. Nothing needs to be -written differently here for that to work, but nothing here may rely on -reaching the host's document either. +host's own document. Compound's stylesheets — the design tokens, the component +styles and the fonts they use — are not here either: as a component, Element +Call shares the host's copy of Compound (it is a peer dependency, so that what +Compound floats into the host's body, tooltips and menus, is styled by the +host's stylesheet), and index.css loads it for the page. + +Everything here is written against Element Call's root element, found by the +`data-element-call-root` attribute useTheme puts on it: the custom properties +are declared on it rather than on `:root`, and the element defaults — the +normalize.css next to this file, and the typography below — take it as a prefix +so that they reach only what is inside it. For the standalone page the root is +the body, so this means what the unscoped rules did; for a component it is the +container the host gave, and the host's document is left as it was. Nothing +rewrites selectors at build time, so a bare `h1` or `.theme` here would reach +the host's page in every build. CSS modules need none of this, since their +class names already confine them. Nothing here should depend on where it lands relative to Element Call's component stylesheets: the bundler decides that, and it decides differently for @@ -32,11 +40,11 @@ the app and for the component build. */ @import url("@fontsource/inconsolata/400.css"); @import url("@fontsource/inconsolata/700.css"); -@import url("normalize.css/normalize.css") layer(normalize); +@import url("./normalize.css") layer(normalize); @import url("@vector-im/compound-design-tokens/assets/web/css/compound-design-tokens.css") layer(compound); @import url("@vector-im/compound-web/dist/style.css") layer(compound.components); -:root { +[data-element-call-root] { --font-scale: 1; --font-size-micro: calc(10px * var(--font-scale)); --font-size-caption: calc(12px * var(--font-scale)); @@ -73,8 +81,8 @@ the app and for the component build. */ --call-view-header-footer-layer: 2; } -:root, -[class*="cpd-theme-"] { +[data-element-call-root], +[data-element-call-root] [class*="cpd-theme-"] { --video-tile-background: var(--cpd-color-bg-subtle-secondary); } @@ -93,7 +101,8 @@ before using one further down the tree. */ container: element-call / size; } -.cpd-theme-dark { +[data-element-call-root].cpd-theme-dark, +[data-element-call-root] .cpd-theme-dark { --cpd-color-border-accent: var(--cpd-color-green-1100); --stopgap-color-on-solid-accent: var(--cpd-color-text-primary); --stopgap-background-85: rgba(16, 19, 23, 0.85); @@ -120,11 +129,6 @@ before using one further down the tree. */ } } -/* We use this to not render the page at all until we know the theme.*/ -.no-theme { - opacity: 0; -} - /* On Android and iOS, prefer native system fonts. The global.css file of Compound Web is where these variables ultimately get consumed to set the page's font-family. */ @@ -138,54 +142,45 @@ font-family. */ } @layer compound-legacy { - h1, - h2, - h3, - h4, - h5, - h6, - p, - a { + [data-element-call-root] :is(h1, h2, h3, h4, h5, h6, p, a) { margin-top: 0; } /* Headline Semi Bold */ - h1 { + [data-element-call-root] h1 { font-weight: 600; font-size: var(--font-size-headline); } /* Title */ - h2 { + [data-element-call-root] h2 { font-weight: 600; font-size: var(--font-size-title); } /* Subtitle */ - h3 { + [data-element-call-root] h3 { font-weight: 600; font-size: var(--font-size-subtitle); } /* Body Semi Bold */ - h4 { + [data-element-call-root] h4 { font-weight: 600; font-size: var(--font-size-body); } - h1, - h2, - h3 { + [data-element-call-root] :is(h1, h2, h3) { line-height: 1.2; } /* Body */ - p { + [data-element-call-root] p { font-size: var(--font-size-body); line-height: var(--font-size-title); } - hr { + [data-element-call-root] hr { width: calc(100% - 24px); border: none; border-top: 1px solid var(--cpd-color-border-interactive-secondary); @@ -199,25 +194,27 @@ font-family. */ margin: 0 12px; } - summary { + [data-element-call-root] summary { font-size: var(--font-size-body); } - details > :not(summary) { + [data-element-call-root] details > :not(summary) { margin-left: var(--font-size-body); } - details[open] > summary { + [data-element-call-root] details[open] > summary { margin-bottom: var(--font-size-body); } } /* normalize.css sets the focus rings on buttons in Firefox to an unusual custom outline, which is inconsistent with our other components and is not sufficiently -visible to be accessible. This resets it back to 'auto'. */ -button:-moz-focusring, -[type="button"]:-moz-focusring, -[type="reset"]:-moz-focusring, -[type="submit"]:-moz-focusring { +visible to be accessible. This resets it back to 'auto'. The root is named in +`:where()`, which adds no specificity, so that the focus styles components set +with a class still win over this. */ +:where([data-element-call-root]) button:-moz-focusring, +:where([data-element-call-root]) [type="button"]:-moz-focusring, +:where([data-element-call-root]) [type="reset"]:-moz-focusring, +:where([data-element-call-root]) [type="submit"]:-moz-focusring { outline: auto; } diff --git a/src/base.css.test.ts b/src/base.css.test.ts new file mode 100644 index 000000000..51180cd0c --- /dev/null +++ b/src/base.css.test.ts @@ -0,0 +1,42 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +import { readFile } from "node:fs/promises"; +import postcss, { type AtRule, type Rule } from "postcss"; +import { describe, expect, it } from "vitest"; + +// As a component, Element Call's stylesheets share a document with the host's. +// CSS modules keep to themselves through their class names, but the base styles +// speak of bare elements and custom properties, and nothing rewrites them at +// build time: each rule has to name the root itself. +const ROOT = "[data-element-call-root]"; + +/** Whether a rule is relative to something other than the document. */ +function isRelative(rule: Rule): boolean { + for (let parent = rule.parent; parent; parent = parent.parent) { + if (parent.type === "rule") return true; + if (parent.type === "atrule") { + const { name } = parent as AtRule; + if (name.endsWith("keyframes") || name === "page") return true; + } + } + return false; +} + +describe.each(["src/base.css", "src/normalize.css"])("%s", (file) => { + it("confines every rule to Element Call's root", async () => { + const root = postcss.parse(await readFile(file, "utf8"), { from: file }); + const unscoped: string[] = []; + root.walkRules((rule) => { + if (isRelative(rule)) return; + for (const selector of rule.selectors) + if (!selector.includes(ROOT)) + unscoped.push(`${rule.source?.start?.line}: ${selector}`); + }); + expect(unscoped).toEqual([]); + }); +}); diff --git a/src/index.css b/src/index.css index 7188f5e79..4f50539b4 100644 --- a/src/index.css +++ b/src/index.css @@ -31,6 +31,13 @@ body.no-scroll-body { width: 100%; } +/* index.html starts the body with this class, so that nothing is painted until +useTheme has decided the theme and removed it. Only the page starts out hidden +like this; a host's container is shown whenever the host shows it. */ +body.no-theme { + opacity: 0; +} + html, body, #root { diff --git a/src/normalize.css b/src/normalize.css new file mode 100644 index 000000000..5494688e0 --- /dev/null +++ b/src/normalize.css @@ -0,0 +1,354 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ + +/* normalize.css, written against Element Call's root element instead of the +document. Upstream's rules are kept word for word, with `html` and `body` +replaced by `[data-element-call-root]` and every other selector prefixed with +it, so that as a component Element Call normalizes only what is inside the +container its host gave it. For the standalone page that container is the +body, so this means what the unscoped stylesheet did. Upstream's `body` +margin is left out: the page's margin is index.css's business, and a host's +container is not ours to reset. + +Diff against upstream's normalize.css to see the changes. */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + +[data-element-call-root] { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Render the `main` element consistently in IE. + */ + +[data-element-call-root] main { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +[data-element-call-root] h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +[data-element-call-root] hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +[data-element-call-root] pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Remove the gray background on active links in IE 10. + */ + +[data-element-call-root] a { + background-color: transparent; +} + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +[data-element-call-root] abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +[data-element-call-root] b, +[data-element-call-root] strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +[data-element-call-root] code, +[data-element-call-root] kbd, +[data-element-call-root] samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font size in all browsers. + */ + +[data-element-call-root] small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +[data-element-call-root] sub, +[data-element-call-root] sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +[data-element-call-root] sub { + bottom: -0.25em; +} + +[data-element-call-root] sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove the border on images inside links in IE 10. + */ + +[data-element-call-root] img { + border-style: none; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + +[data-element-call-root] button, +[data-element-call-root] input, +[data-element-call-root] optgroup, +[data-element-call-root] select, +[data-element-call-root] textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +[data-element-call-root] button, +[data-element-call-root] input { + /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +[data-element-call-root] button, +[data-element-call-root] select { + /* 1 */ + text-transform: none; +} + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ + +[data-element-call-root] button, +[data-element-call-root] [type="button"], +[data-element-call-root] [type="reset"], +[data-element-call-root] [type="submit"] { + -webkit-appearance: button; +} + +/** + * Remove the inner border and padding in Firefox. + */ + +[data-element-call-root] button::-moz-focus-inner, +[data-element-call-root] [type="button"]::-moz-focus-inner, +[data-element-call-root] [type="reset"]::-moz-focus-inner, +[data-element-call-root] [type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +[data-element-call-root] button:-moz-focusring, +[data-element-call-root] [type="button"]:-moz-focusring, +[data-element-call-root] [type="reset"]:-moz-focusring, +[data-element-call-root] [type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +[data-element-call-root] fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +[data-element-call-root] legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +[data-element-call-root] progress { + vertical-align: baseline; +} + +/** + * Remove the default vertical scrollbar in IE 10+. + */ + +[data-element-call-root] textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + +[data-element-call-root] [type="checkbox"], +[data-element-call-root] [type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[data-element-call-root] [type="number"]::-webkit-inner-spin-button, +[data-element-call-root] [type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[data-element-call-root] [type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ + +[data-element-call-root] [type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +[data-element-call-root] ::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + +[data-element-call-root] details { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +[data-element-call-root] summary { + display: list-item; +} + +/* Misc + ========================================================================== */ + +/** + * Add the correct display in IE 10+. + */ + +[data-element-call-root] template { + display: none; +} + +/** + * Add the correct display in IE 10. + */ + +[data-element-call-root] [hidden] { + display: none; +} diff --git a/vite-component-dev.config.ts b/vite-component-dev.config.ts index 804b3d1f1..ffaf9600d 100644 --- a/vite-component-dev.config.ts +++ b/vite-component-dev.config.ts @@ -10,7 +10,6 @@ import { realpathSync } from "node:fs"; import * as fs from "node:fs"; import { vitePluginsConfig } from "./vite.config.ts"; -import { scopeStylesToRoot } from "./component/build/scopeStylesToRoot"; // Serves the harness under `component/dev`, which embeds Element Call as a // component the way a host application would. Development only: this is not @@ -36,9 +35,6 @@ export default defineConfig(({ mode }) => { return { ...vitePluginsConfig({ mode, html: false }), root: "component/dev", - // The same scoping the library build applies, so the harness shows what a - // host will get — including whether its own page is left alone - css: { postcss: { plugins: [scopeStylesToRoot()] } }, // So that the harness can read the same config.json the standalone app // does, if the developer has written one publicDir: "../../public", diff --git a/vite-component.config.ts b/vite-component.config.ts index 09baac23c..f9e19d434 100644 --- a/vite-component.config.ts +++ b/vite-component.config.ts @@ -8,7 +8,6 @@ Please see LICENSE in the repository root for full details. import { defineConfig } from "vite"; import { vitePluginsConfig } from "./vite.config.ts"; -import { scopeStylesToRoot } from "./component/build/scopeStylesToRoot"; // Config for Element Call as a React component, to be imported by an // application embedding it rather than served as a page of its own. @@ -34,9 +33,6 @@ export default defineConfig(({ mode }) => { // whatever is in `public` — including the developer's own config.json, which // is not in the repository — into the output we would publish. publicDir: false, - // A host's document is not ours to style: everything in the stylesheet is - // confined to the element Element Call is mounted in - css: { postcss: { plugins: [scopeStylesToRoot()] } }, build: { // Into the package directory, so that `component/package.json` describes // what sits next to it and the directory can be installed as a package