mirror of
https://github.com/vector-im/element-call.git
synced 2026-10-10 23:25:52 +00:00
Stop bundling Compound, let the host supply it
This commit is contained in:
@@ -306,8 +306,14 @@ directory target it rather than the repository; run them from the repository
|
|||||||
root. The host imports the component from
|
root. The host imports the component from
|
||||||
`@element-hq/element-call-component` and the stylesheet from
|
`@element-hq/element-call-component` and the stylesheet from
|
||||||
`@element-hq/element-call-component/style.css`, and has to provide `react`,
|
`@element-hq/element-call-component/style.css`, and has to provide `react`,
|
||||||
`react-dom`, `matrix-js-sdk` and `livekit-client` itself, since the bundle leaves
|
`react-dom`, `matrix-js-sdk`, `livekit-client`, `@vector-im/compound-web` and
|
||||||
them external.
|
`@vector-im/compound-design-tokens` itself, since the bundle leaves them
|
||||||
|
external. Compound's stylesheet (`@vector-im/compound-web/dist/style.css`), the
|
||||||
|
design tokens' CSS and the Inter and Inconsolata fonts are the host's to load
|
||||||
|
too: the component shares the host's copy of Compound, so that the tooltips and
|
||||||
|
menus Compound floats into the host's body are styled by the host's stylesheet
|
||||||
|
like everything else there, and the component's own stylesheet stays confined
|
||||||
|
to its root.
|
||||||
|
|
||||||
The component is large, and a host will usually load it lazily, only once a
|
The component is large, and a host will usually load it lazily, only once a
|
||||||
call is shown. Everything a host needs in order to talk about a call before
|
call is shown. Everything a host needs in order to talk about a call before
|
||||||
|
|||||||
@@ -34,21 +34,14 @@ component rather than a page makes. */
|
|||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* floating-ui's portal node, which `PortalRoot` puts here to hold the call
|
/* The call itself. It is a stacking context of its own, as the standalone
|
||||||
along with the tooltips Compound floats over it. It fills the container, and
|
page's `#root` is, so that the layers the call view uses for its header, footer
|
||||||
lays out its children as the container would. */
|
and overlays stay below the modals and toasts, which are portalled into the
|
||||||
.root > [data-floating-ui-portal] {
|
root and carry no layer of their own.
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
block-size: 100%;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The call itself, in the wrapper `PortalRoot` gives it. It is a stacking
|
What Compound floats — tooltips, menus — is not in here at all: it goes into
|
||||||
context of its own, as the standalone page's `#root` is, so that the layers
|
the host's body, as it would for any other component on the host's page, and is
|
||||||
the call view uses for its header, footer and overlays stay below the modals
|
styled and layered by the host's copy of Compound. */
|
||||||
and toasts, which are portalled into the root and carry no layer of their own,
|
|
||||||
and below the tooltips. */
|
|
||||||
.content {
|
.content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -56,11 +49,3 @@ and below the tooltips. */
|
|||||||
position: relative;
|
position: relative;
|
||||||
isolation: isolate;
|
isolation: isolate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The tooltips sit in portal nodes of their own, nested in ours. Raised, so
|
|
||||||
that a tooltip opened inside a modal shows above it: the modal is portalled
|
|
||||||
straight into the root and so comes later in the document than our node. */
|
|
||||||
.root > [data-floating-ui-portal] > [data-floating-ui-portal] {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,43 +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 { render, screen } from "@testing-library/react";
|
|
||||||
import { Tooltip, TooltipProvider } from "@vector-im/compound-web";
|
|
||||||
import { afterEach, describe, expect, test } from "vitest";
|
|
||||||
|
|
||||||
import { PortalRoot } from "./PortalRoot";
|
|
||||||
|
|
||||||
describe("PortalRoot", () => {
|
|
||||||
const root = document.createElement("div");
|
|
||||||
document.body.appendChild(root);
|
|
||||||
afterEach(() => root.replaceChildren());
|
|
||||||
|
|
||||||
// A label tooltip is in the DOM from the start, hidden by the stylesheet until
|
|
||||||
// hovered, which is what makes it show as bare text when it lands outside the
|
|
||||||
// root
|
|
||||||
const tree = (
|
|
||||||
<TooltipProvider>
|
|
||||||
<Tooltip label="Mute microphone">
|
|
||||||
<button>mic</button>
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
);
|
|
||||||
|
|
||||||
test("Compound's tooltips float into the root", () => {
|
|
||||||
render(<PortalRoot root={root}>{tree}</PortalRoot>);
|
|
||||||
expect(root).toContainElement(
|
|
||||||
screen.getByRole("button", { name: "Mute microphone" }),
|
|
||||||
);
|
|
||||||
expect(root).toContainElement(screen.getByText("Mute microphone"));
|
|
||||||
});
|
|
||||||
|
|
||||||
test("without it they float into the body", () => {
|
|
||||||
render(tree);
|
|
||||||
expect(root).not.toContainElement(screen.getByText("Mute microphone"));
|
|
||||||
expect(document.body).toContainElement(screen.getByText("Mute microphone"));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,44 +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 { FloatingPortal } from "@floating-ui/react";
|
|
||||||
import { type FC, type ReactNode } from "react";
|
|
||||||
|
|
||||||
import styles from "./ElementCall.module.css";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Keeps what Compound floats over Element Call, its tooltips, inside Element
|
|
||||||
* Call's root.
|
|
||||||
*
|
|
||||||
* Compound's `Tooltip` renders its bubble through floating-ui's
|
|
||||||
* `FloatingPortal`, which appends to `document.body` unless it sits inside
|
|
||||||
* another `FloatingPortal`, whose node it then shares. Compound offers no way
|
|
||||||
* to name a container, so this is that enclosing portal: it puts its node
|
|
||||||
* inside `root` and renders the children into it, and every tooltip they open
|
|
||||||
* lands in the same node, inside the element the component's stylesheet is
|
|
||||||
* confined to. Outside it, a tooltip matches none of the component's rules and
|
|
||||||
* shows as bare text, permanently, since the rule that hides a label tooltip
|
|
||||||
* until it is hovered is one of them.
|
|
||||||
*
|
|
||||||
* The children go in a wrapper of their own, which stands in for the `#root`
|
|
||||||
* of the standalone page: it fills the node and is a stacking context, so
|
|
||||||
* that the call's own layers (its header and footer, its overlays) stay below
|
|
||||||
* the modals and toasts portalled into the root, and below the tooltips beside
|
|
||||||
* it. `ElementCall.module.css` lays both out.
|
|
||||||
*
|
|
||||||
* Nothing renders until `root` exists, just as nothing renders before Element
|
|
||||||
* Call's container does. The portal's tab order guards are for a focus
|
|
||||||
* manager, which nothing in Element Call puts here, so they are off.
|
|
||||||
*/
|
|
||||||
export const PortalRoot: FC<{
|
|
||||||
root: HTMLElement | null;
|
|
||||||
children: ReactNode;
|
|
||||||
}> = ({ root, children }) => (
|
|
||||||
<FloatingPortal root={root} preserveTabOrder={false}>
|
|
||||||
<div className={styles.content}>{children}</div>
|
|
||||||
</FloatingPortal>
|
|
||||||
);
|
|
||||||
@@ -40,10 +40,10 @@ const IS_ROOT = `:where(${ROOT_SELECTOR})`;
|
|||||||
* Element Call as a component.
|
* Element Call as a component.
|
||||||
*
|
*
|
||||||
* As a page of its own, Element Call can style the document: normalize.css and
|
* As a page of its own, Element Call can style the document: normalize.css and
|
||||||
* Compound speak of `html`, `body` and bare elements, and the design tokens are
|
* its own base styles speak of `html`, `body` and bare elements, and its custom
|
||||||
* declared on `:root`. As a component, all of that would land on the host's
|
* properties are declared on `:root`. As a component, all of that would land on
|
||||||
* document too. This rewrites every selector so that it matches only the root
|
* the host's document too. This rewrites every selector so that it matches only
|
||||||
* or its descendants:
|
* the root or its descendants:
|
||||||
*
|
*
|
||||||
* - `html`, `body` and `:root` become the root element, which is what stands in
|
* - `html`, `body` and `:root` become the root element, which is what stands in
|
||||||
* for the document inside a host.
|
* for the document inside a host.
|
||||||
@@ -55,9 +55,10 @@ const IS_ROOT = `:where(${ROOT_SELECTOR})`;
|
|||||||
* CSS modules are scoped by their class names already, so only their selectors
|
* CSS modules are scoped by their class names already, so only their selectors
|
||||||
* that would match by element alone — `pre` rather than `.pre` — are touched.
|
* that would match by element alone — `pre` rather than `.pre` — are touched.
|
||||||
*
|
*
|
||||||
* The root's fonts and design tokens are still inherited by everything inside
|
* The root's custom properties are still inherited by everything inside it,
|
||||||
* it, the way they were from `body` and `:root`, and `@font-face` declarations
|
* the way they were from `:root`. Compound's own stylesheet — the design
|
||||||
* stay global, which they are by nature.
|
* tokens, the component styles, the fonts — is not in this build at all: the
|
||||||
|
* host supplies it, unscoped, along with its copy of Compound.
|
||||||
*/
|
*/
|
||||||
export function scopeStylesToRoot(): Plugin {
|
export function scopeStylesToRoot(): Plugin {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -9,6 +9,16 @@ import { StrictMode } from "react";
|
|||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import { logger } from "matrix-js-sdk/lib/logger";
|
import { logger } from "matrix-js-sdk/lib/logger";
|
||||||
|
|
||||||
|
// What a host provides along with its copy of Compound: its stylesheet, the
|
||||||
|
// design tokens and the fonts they name. The component does not bundle them.
|
||||||
|
import "@fontsource/inter/400.css";
|
||||||
|
import "@fontsource/inter/500.css";
|
||||||
|
import "@fontsource/inter/600.css";
|
||||||
|
import "@fontsource/inter/700.css";
|
||||||
|
import "@fontsource/inconsolata/400.css";
|
||||||
|
import "@fontsource/inconsolata/700.css";
|
||||||
|
import "@vector-im/compound-design-tokens/assets/web/css/compound-design-tokens.css";
|
||||||
|
import "@vector-im/compound-web/dist/style.css";
|
||||||
import { type ConfigOptions, initializeElementCall } from "../index";
|
import { type ConfigOptions, initializeElementCall } from "../index";
|
||||||
import { Harness } from "./Harness";
|
import { Harness } from "./Harness";
|
||||||
// After Element Call's, so that the host has the last word on its own page
|
// After Element Call's, so that the host has the last word on its own page
|
||||||
|
|||||||
+13
-6
@@ -17,19 +17,25 @@ Please see LICENSE in the repository root for full details.
|
|||||||
* host instead, or is confined to the container it is mounted in.
|
* host instead, or is confined to the container it is mounted in.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// The design tokens, fonts and element defaults every Element Call stylesheet
|
// The element defaults and custom properties every Element Call stylesheet
|
||||||
// builds on. Written for a page, they speak of `html`, `body` and bare
|
// builds on. Written for a page, they speak of `html`, `body` and bare
|
||||||
// elements; the component build confines them, and every other stylesheet in
|
// elements; the component build confines them, and every other stylesheet in
|
||||||
// this bundle, to the root element below (see build/scopeStylesToRoot.ts), so
|
// this bundle, to the root element below (see build/scopeStylesToRoot.ts), so
|
||||||
// that the host's document is left as it was.
|
// that the host's document is left as it was.
|
||||||
//
|
//
|
||||||
|
// Compound is not in here. The component uses the host's copy of it (a peer
|
||||||
|
// dependency, left external by the build), and the host loads Compound's
|
||||||
|
// stylesheet, design tokens and fonts along with it: what Compound floats into
|
||||||
|
// the host's body — tooltips, menus — is then styled by the same stylesheet as
|
||||||
|
// everything else there, and Element Call's root takes the tokens and fonts of
|
||||||
|
// the theme class it carries, as any element on the host's page would.
|
||||||
|
//
|
||||||
// Where these land relative to the component stylesheets is the bundler's
|
// Where these land relative to the component stylesheets is the bundler's
|
||||||
// choice — the standalone app puts them first, this build puts them in the
|
// choice — the standalone app puts them first, this build puts them in the
|
||||||
// middle — so nothing in base.css may depend on winning or losing against a
|
// middle — so nothing in base.css may depend on winning or losing against a
|
||||||
// component's own rules at equal specificity. It currently does not: what it
|
// component's own rules at equal specificity. It currently does not: what it
|
||||||
// declares unlayered is custom properties on Element Call's root, which
|
// declares unlayered is custom properties on Element Call's root, which
|
||||||
// components inherit rather than compete with, and everything from Compound
|
// components inherit rather than compete with.
|
||||||
// sits in a `@layer`, which loses to unlayered rules either way.
|
|
||||||
import "../src/base.css";
|
import "../src/base.css";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -57,7 +63,6 @@ import { ErrorPage } from "../src/FullScreenView";
|
|||||||
import { ClientProvider } from "../src/ClientContext";
|
import { ClientProvider } from "../src/ClientContext";
|
||||||
import { HostBridgeProvider } from "../src/HostBridge";
|
import { HostBridgeProvider } from "../src/HostBridge";
|
||||||
import { RootElementProvider, useRootElement } from "../src/RootElementContext";
|
import { RootElementProvider, useRootElement } from "../src/RootElementContext";
|
||||||
import { PortalRoot } from "./PortalRoot";
|
|
||||||
import {
|
import {
|
||||||
configurationForIntent,
|
configurationForIntent,
|
||||||
componentProperties,
|
componentProperties,
|
||||||
@@ -232,7 +237,9 @@ export const ElementCall: FC<ElementCallProps> = ({
|
|||||||
<UrlParamsProvider value={params}>
|
<UrlParamsProvider value={params}>
|
||||||
<div ref={setContainer} className={styles.root}>
|
<div ref={setContainer} className={styles.root}>
|
||||||
{ready && (
|
{ready && (
|
||||||
<PortalRoot root={container}>
|
// Stands in for the standalone page's `#root`: see `.content`
|
||||||
|
// in ElementCall.module.css
|
||||||
|
<div className={styles.content}>
|
||||||
<RootElementProvider value={container}>
|
<RootElementProvider value={container}>
|
||||||
{/* Whatever goes wrong in here is shown in here. Left to
|
{/* Whatever goes wrong in here is shown in here. Left to
|
||||||
propagate, an error would unmount the host's own tree. */}
|
propagate, an error would unmount the host's own tree. */}
|
||||||
@@ -261,7 +268,7 @@ export const ElementCall: FC<ElementCallProps> = ({
|
|||||||
</Decoration>
|
</Decoration>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</RootElementProvider>
|
</RootElementProvider>
|
||||||
</PortalRoot>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</UrlParamsProvider>
|
</UrlParamsProvider>
|
||||||
|
|||||||
@@ -38,6 +38,8 @@
|
|||||||
"prepare": "cd .. && pnpm install --frozen-lockfile && pnpm build:component"
|
"prepare": "cd .. && pnpm install --frozen-lockfile && pnpm build:component"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
"@vector-im/compound-design-tokens": "^11.0.0",
|
||||||
|
"@vector-im/compound-web": "^10.0.0",
|
||||||
"livekit-client": "^2.18.1",
|
"livekit-client": "^2.18.1",
|
||||||
"matrix-js-sdk": "*",
|
"matrix-js-sdk": "*",
|
||||||
"react": "^19",
|
"react": "^19",
|
||||||
|
|||||||
@@ -76,7 +76,10 @@ Code that builds in only one is a bug.
|
|||||||
- `build:component` — `@element-hq/element-call-component`, sources in `component/`
|
- `build:component` — `@element-hq/element-call-component`, sources in `component/`
|
||||||
(its own pnpm project; run pnpm from the repo root). Host API is in the README;
|
(its own pnpm project; run pnpm from the repo root). Host API is in the README;
|
||||||
`pnpm lint:externals` rejects an import of a `react` / `react-dom` /
|
`pnpm lint:externals` rejects an import of a `react` / `react-dom` /
|
||||||
`matrix-js-sdk` / `livekit-client` subpath the externals list omits. Two entry
|
`matrix-js-sdk` / `livekit-client` / `@vector-im/compound-web` /
|
||||||
|
`@vector-im/compound-design-tokens` subpath the externals list omits.
|
||||||
|
Compound's stylesheets are loaded by `src/index.css` for the page and by the
|
||||||
|
host for the component, never by `src/base.css`. Two entry
|
||||||
points: `index.tsx` (the component) and `api.ts` (types, enums and
|
points: `index.tsx` (the component) and `api.ts` (types, enums and
|
||||||
`configurationForIntent`, for a host that must not load the component). `api.ts`
|
`configurationForIntent`, for a host that must not load the component). `api.ts`
|
||||||
may only reach types and constants — `src/UrlConfiguration.ts`, not `UrlParams`.
|
may only reach types and constants — `src/UrlConfiguration.ts`, not `UrlParams`.
|
||||||
|
|||||||
@@ -48,7 +48,6 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@codecov/vite-plugin": "^1.3.0",
|
"@codecov/vite-plugin": "^1.3.0",
|
||||||
"@floating-ui/react": "^0.27.0",
|
|
||||||
"@fontsource/inconsolata": "^5.1.0",
|
"@fontsource/inconsolata": "^5.1.0",
|
||||||
"@fontsource/inter": "^5.1.0",
|
"@fontsource/inter": "^5.1.0",
|
||||||
"@formatjs/intl-durationformat": "^0.10.0",
|
"@formatjs/intl-durationformat": "^0.10.0",
|
||||||
|
|||||||
Generated
-3
@@ -25,9 +25,6 @@ importers:
|
|||||||
'@codecov/vite-plugin':
|
'@codecov/vite-plugin':
|
||||||
specifier: ^1.3.0
|
specifier: ^1.3.0
|
||||||
version: 1.9.1(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))
|
version: 1.9.1(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))
|
||||||
'@floating-ui/react':
|
|
||||||
specifier: ^0.27.0
|
|
||||||
version: 0.27.20(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
|
|
||||||
'@fontsource/inconsolata':
|
'@fontsource/inconsolata':
|
||||||
specifier: ^5.1.0
|
specifier: ^5.1.0
|
||||||
version: 5.3.0
|
version: 5.3.0
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ Please see LICENSE in the repository root for full details.
|
|||||||
* Checks that the component build leaves the packages a host must supply to
|
* Checks that the component build leaves the packages a host must supply to
|
||||||
* the host.
|
* the host.
|
||||||
*
|
*
|
||||||
* A host application already has React, the Matrix SDK and LiveKit, and a
|
* A host application already has React, the Matrix SDK, LiveKit and Compound,
|
||||||
* second copy of any of them is worse than dead weight: React would hold two
|
* and a second copy of any of them is worse than dead weight: React would hold
|
||||||
* sets of hooks, and the Matrix client would run two sync loops. So the
|
* two sets of hooks, the Matrix client would run two sync loops, and a second
|
||||||
|
* Compound would style the tooltips it floats into the host's body with class
|
||||||
|
* names the host's stylesheet does not know. So the
|
||||||
* component build lists them as external — but that list has to name every
|
* component build lists them as external — but that list has to name every
|
||||||
* subpath, since the bundler silently ignores the pattern and callback forms
|
* subpath, since the bundler silently ignores the pattern and callback forms
|
||||||
* of the option, and an import it does not cover is bundled with no warning at
|
* of the option, and an import it does not cover is bundled with no warning at
|
||||||
@@ -33,6 +35,9 @@ import { loadConfigFromFile } from "vite";
|
|||||||
|
|
||||||
const CONFIG = "vite-component.config.ts";
|
const CONFIG = "vite-component.config.ts";
|
||||||
const SOURCES = ["src", "component"];
|
const SOURCES = ["src", "component"];
|
||||||
|
// The development harness is a host, not part of the component: it is the
|
||||||
|
// one that imports what a host supplies (Compound's stylesheets, say).
|
||||||
|
const EXCLUDED = ["component/dev"];
|
||||||
|
|
||||||
/** The packages whose duplication would break a host, rather than merely enlarge it. */
|
/** The packages whose duplication would break a host, rather than merely enlarge it. */
|
||||||
const MUST_BE_EXTERNAL = [
|
const MUST_BE_EXTERNAL = [
|
||||||
@@ -40,6 +45,8 @@ const MUST_BE_EXTERNAL = [
|
|||||||
"react-dom",
|
"react-dom",
|
||||||
"matrix-js-sdk",
|
"matrix-js-sdk",
|
||||||
"livekit-client",
|
"livekit-client",
|
||||||
|
"@vector-im/compound-web",
|
||||||
|
"@vector-im/compound-design-tokens",
|
||||||
];
|
];
|
||||||
|
|
||||||
const isTestFile = (name) =>
|
const isTestFile = (name) =>
|
||||||
@@ -49,6 +56,7 @@ const isTestFile = (name) =>
|
|||||||
async function* sourceFiles(dir) {
|
async function* sourceFiles(dir) {
|
||||||
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
||||||
const path = join(dir, entry.name);
|
const path = join(dir, entry.name);
|
||||||
|
if (EXCLUDED.includes(path)) continue;
|
||||||
if (entry.isDirectory()) yield* sourceFiles(path);
|
if (entry.isDirectory()) yield* sourceFiles(path);
|
||||||
else if (/\.(ts|tsx)$/.test(entry.name) && !isTestFile(entry.name))
|
else if (/\.(ts|tsx)$/.test(entry.name) && !isTestFile(entry.name))
|
||||||
yield path;
|
yield path;
|
||||||
|
|||||||
+15
-19
@@ -5,19 +5,24 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|||||||
Please see LICENSE in the repository root for full details.
|
Please see LICENSE in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* The styles Element Call needs wherever it is shown: the design tokens, fonts
|
/* The styles Element Call needs wherever it is shown: the element defaults and
|
||||||
and element defaults its own stylesheets build on top of.
|
custom properties its own stylesheets build on top of.
|
||||||
|
|
||||||
Split out from index.css so that Element Call as a component can have these
|
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
|
without also being given the standalone page's layout, which would style the
|
||||||
host's own document. What remains here still speaks of the
|
host's own document. Compound's stylesheets — the design tokens, the component
|
||||||
document — normalize.css and the typography below use bare element selectors,
|
styles and the fonts they use — are not here either: as a component, Element
|
||||||
and the custom properties are declared on `:root` — which is right for the
|
Call shares the host's copy of Compound (it is a peer dependency, so that what
|
||||||
page, and is why the component build rewrites it: there every selector is
|
Compound floats into the host's body, tooltips and menus, is styled by the
|
||||||
confined to Element Call's root element (see component/build/scopeStylesToRoot.ts),
|
host's stylesheet), and index.css loads it for the page.
|
||||||
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
|
What remains here still speaks of the document — normalize.css and the
|
||||||
reaching the host's document either.
|
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.
|
||||||
|
|
||||||
Nothing here should depend on where it lands relative to Element Call's
|
Nothing here should depend on where it lands relative to Element Call's
|
||||||
component stylesheets: the bundler decides that, and it decides differently for
|
component stylesheets: the bundler decides that, and it decides differently for
|
||||||
@@ -25,16 +30,7 @@ the app and for the component build. */
|
|||||||
|
|
||||||
@layer normalize, compound-legacy, compound;
|
@layer normalize, compound-legacy, compound;
|
||||||
|
|
||||||
@import url("@fontsource/inter/400.css");
|
|
||||||
@import url("@fontsource/inter/500.css");
|
|
||||||
@import url("@fontsource/inter/600.css");
|
|
||||||
@import url("@fontsource/inter/700.css");
|
|
||||||
@import url("@fontsource/inconsolata/400.css");
|
|
||||||
@import url("@fontsource/inconsolata/700.css");
|
|
||||||
|
|
||||||
@import url("normalize.css/normalize.css") layer(normalize);
|
@import url("normalize.css/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 {
|
:root {
|
||||||
--font-scale: 1;
|
--font-scale: 1;
|
||||||
|
|||||||
+15
-1
@@ -7,10 +7,24 @@ Please see LICENSE in the repository root for full details.
|
|||||||
|
|
||||||
/* Styles for Element Call as a page of its own. The parts that apply wherever
|
/* Styles for Element Call as a page of its own. The parts that apply wherever
|
||||||
Element Call is shown live in base.css; these are about owning the document,
|
Element Call is shown live in base.css; these are about owning the document,
|
||||||
and are not loaded when a host embeds Element Call as a component. */
|
and are not loaded when a host embeds Element Call as a component.
|
||||||
|
|
||||||
|
That includes Compound: the page brings the design tokens, the component styles
|
||||||
|
and their fonts, where a host embedding the component supplies its own. The
|
||||||
|
`compound` layers are ordered by base.css. */
|
||||||
|
|
||||||
@import url("./base.css");
|
@import url("./base.css");
|
||||||
|
|
||||||
|
@import url("@fontsource/inter/400.css");
|
||||||
|
@import url("@fontsource/inter/500.css");
|
||||||
|
@import url("@fontsource/inter/600.css");
|
||||||
|
@import url("@fontsource/inter/700.css");
|
||||||
|
@import url("@fontsource/inconsolata/400.css");
|
||||||
|
@import url("@fontsource/inconsolata/700.css");
|
||||||
|
|
||||||
|
@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);
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: var(--cpd-color-bg-canvas-default);
|
background-color: var(--cpd-color-bg-canvas-default);
|
||||||
color: var(--cpd-color-text-primary);
|
color: var(--cpd-color-text-primary);
|
||||||
|
|||||||
@@ -65,8 +65,12 @@ export default defineConfig(({ mode }) => {
|
|||||||
},
|
},
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
// The host already has these, and a second copy of any of them does not
|
// The host already has these, and a second copy of any of them does not
|
||||||
// merely bloat the bundle: React would hold two sets of hooks, and the
|
// merely bloat the bundle: React would hold two sets of hooks, the
|
||||||
// Matrix client would run two sync loops.
|
// Matrix client would run two sync loops, and a second Compound would
|
||||||
|
// bring a second stylesheet with class names of its own — so that what
|
||||||
|
// it floats into the host's body, tooltips and menus, would match the
|
||||||
|
// host's Compound stylesheet only when the two happened to be the same
|
||||||
|
// version.
|
||||||
//
|
//
|
||||||
// Every subpath has to be named. Element Call reaches most of the Matrix
|
// Every subpath has to be named. Element Call reaches most of the Matrix
|
||||||
// SDK as `matrix-js-sdk/lib/…`, and a bare "matrix-js-sdk" would not
|
// SDK as `matrix-js-sdk/lib/…`, and a bare "matrix-js-sdk" would not
|
||||||
@@ -103,6 +107,8 @@ export default defineConfig(({ mode }) => {
|
|||||||
"matrix-js-sdk/lib/sync",
|
"matrix-js-sdk/lib/sync",
|
||||||
"matrix-js-sdk/lib/types",
|
"matrix-js-sdk/lib/types",
|
||||||
"matrix-js-sdk/lib/utils",
|
"matrix-js-sdk/lib/utils",
|
||||||
|
"@vector-im/compound-web",
|
||||||
|
"@vector-im/compound-design-tokens/assets/web/icons",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user