Skip to content

Commit 29499aa

Browse files
committed
Add injectDefaultA2uiTheme function to web_core defining overridable CSS variables for theming.
1 parent 49df893 commit 29499aa

2 files changed

Lines changed: 115 additions & 0 deletions

File tree

renderers/web_core/src/v0_9/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export * from './state/surface-components-model.js';
3535
export * from './state/surface-group-model.js';
3636
export * from './state/surface-model.js';
3737
export * from './errors.js';
38+
export { injectDefaultA2uiTheme } from './styles/default.js';
3839

3940
export {effect, Signal, signal} from '@preact/signals-core';
4041

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* The actual CSS markup of the default A2UI theme.
19+
*
20+
* This should be only variable definitions, so they can pierce into the
21+
* shadow DOM of components.
22+
*
23+
* It uses `:where()` to ensure zero specificity, allowing page styles to
24+
* override these defaults as needed without having to deal with specificity.
25+
*
26+
* Defines a dark mode variant by setting the `a2ui-dark` class on the element.
27+
*/
28+
const DEFAULT_CSS = `
29+
:where(:root) {
30+
color-scheme: light;
31+
--a2ui-color-background: light-dark(#eee, #111);
32+
--a2ui-color-on-background: light-dark(#333, #eee);
33+
34+
--a2ui-color-primary: #17e;
35+
--a2ui-color-primary-light: color-mix(in oklab, var(--a2ui-color-primary) 85%, white);
36+
--a2ui-color-primary-dark: color-mix(in oklab, var(--a2ui-color-primary) 85%, black);
37+
--a2ui-color-primary-hover: light-dark(var(--a2ui-color-primary-dark), var(--a2ui-color-primary-light));
38+
--a2ui-color-on-primary: #fff;
39+
40+
--a2ui-color-secondary: light-dark(#ddd, #333);
41+
--a2ui-color-secondary-light: color-mix(in oklab, var(--a2ui-color-secondary) 85%, white);
42+
--a2ui-color-secondary-dark: color-mix(in oklab, var(--a2ui-color-secondary) 95%, black);
43+
--a2ui-color-secondary-hover: light-dark(var(--a2ui-color-secondary-dark), var(--a2ui-color-secondary-light));
44+
--a2ui-color-on-secondary: light-dark(#333, #eee);
45+
46+
--a2ui-border-radius: 0.25rem;
47+
--a2ui-color-border: light-dark(#ccc, #444);
48+
--a2ui-border-width: 1px;
49+
--a2ui-border: 1px solid var(--a2ui-color-border, #ccc);
50+
51+
--a2ui-grid-base: 0.5rem;
52+
--a2ui-spacing-xs: calc(var(--a2ui-spacing-s) / 2);
53+
--a2ui-spacing-s: calc(var(--a2ui-spacing-m) / 2);
54+
--a2ui-spacing-m: var(--a2ui-grid-base);
55+
--a2ui-spacing-l: calc(var(--a2ui-spacing-m) * 2);
56+
--a2ui-spacing-xl: calc(var(--a2ui-spacing-l) * 2);
57+
58+
--a2ui-font-size: 1rem;
59+
--a2ui-font-scale: 1.2;
60+
--a2ui-font-size-xs: calc(var(--a2ui-font-size-s) / var(--a2ui-font-scale));
61+
--a2ui-font-size-s: calc(var(--a2ui-font-size-m) / var(--a2ui-font-scale));
62+
--a2ui-font-size-m: var(--a2ui-font-size);
63+
--a2ui-font-size-l: calc(var(--a2ui-font-size-m) * var(--a2ui-font-scale));
64+
--a2ui-font-size-xl: calc(var(--a2ui-font-size-l) * var(--a2ui-font-scale));
65+
--a2ui-font-size-2xl: calc(var(--a2ui-font-size-xl) * var(--a2ui-font-scale));
66+
67+
--a2ui-line-height-headings: 1.2;
68+
--a2ui-line-height-body: 1.5;
69+
}
70+
71+
:where(.a2ui-dark) {
72+
color-scheme: dark;
73+
}
74+
`;
75+
76+
/**
77+
* Caches the default stylesheet so it is only created once.
78+
*/
79+
let defaultStyleSheet: CSSStyleSheet | undefined;
80+
81+
/**
82+
* Retrieves the default CSSStyleSheet for A2UI components.
83+
*
84+
* If the stylesheet doesn't exist, it creates and initializes one with default
85+
* theme variables from the DEFAULT_CSS string.
86+
*
87+
* @returns The default CSSStyleSheet used by A2UI.
88+
*/
89+
function getDefaultStyleSheet(): CSSStyleSheet {
90+
if (!defaultStyleSheet) {
91+
defaultStyleSheet = new CSSStyleSheet();
92+
defaultStyleSheet.replaceSync(DEFAULT_CSS);
93+
}
94+
return defaultStyleSheet;
95+
}
96+
97+
/**
98+
* Injects the default A2UI theme variables into the document.
99+
*
100+
* This method is used by the A2UI-provided basic catalogs of each renderer,
101+
* as needed, so design token values can be shared across all of them.
102+
*
103+
* End users may redefine the CSS variables defined in the default stylesheet
104+
* to customize the theme.
105+
*
106+
* This method ensures the default stylesheet is added to the root document
107+
* of the page, if it's not already present.
108+
*/
109+
export function injectDefaultA2uiTheme() {
110+
const sheet = getDefaultStyleSheet();
111+
if (!document.adoptedStyleSheets.includes(sheet)) {
112+
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
113+
}
114+
}

0 commit comments

Comments
 (0)