How a TCE looks and how its CSS is structured. It merges the site-wide style guidelines with the rules specific to a Shadow-DOM element.
Every element attaches an open Shadow Root in its constructor
(Lifecycle). Styles written for the element
cannot leak out, and the host page's styles cannot leak in — other than inherited properties
(font-family, color) and CSS custom properties, which do pierce the boundary. No ad-hoc
class-name prefixing is needed; the boundary does that job.
An element fills the box it is given: :host { display: block | flex; width: 100%; height: 100%; box-sizing: border-box; }.
The window, the play page and the dev harness size that box; the element never sizes itself to
a fixed pixel size.
One src/applet.scss per element, imported as applet.scss?inline, with
BEM class names (.block__element--modifier), e.g. .notepad__toolbar,
.applet__button. Packages ship and version independently, so no SCSS partial is shared across
packages — the palette is restated as custom properties in each element.
Simple, low-stimulus, Windows95-esque:
| Name | Hex | Use |
|---|---|---|
| Turquoise | #6cdad9 |
Primary/interesting backgrounds |
| Gray | #6c6c6c |
Borders, functional backgrounds |
| Blue | #26306a |
Alternative text color / highlight |
| Orange | #d48c17 |
Alternative background / highlight |
| Black | #111111 |
Text color, clear-cut borders |
| White | #ffffff |
Surfaces, light bevel edges |
Declared once per element as custom properties on :host (--color-turquoise, …) and used
from there — never hardcoded repeatedly, and never expected from the host page, since an element
may be mounted on a bare play page with no page styling at all.
Every package has a 64×64 icon.svg in the flat palette above, with shape-rendering: crispEdges
and no gradients, drop shadows or embedded fonts. It is uploaded with the package and shown on
the desktop, in the package manager and in the registry.
Monospaced fonts. Animation frame rates stay low (6–15 fps) by design — it is the aesthetic, not a fallback.
Styles are attached through the one shared helper, attach_styles from @tce-v3/common,
in the References constructor:
export async function attach_styles(
shadow: ShadowRoot,
source: { css: string } | { url: string },
): Promise<void>;
attach_styles(this.shadow, { css: applet_scss }); // the ?inline SCSS every element uses
It uses CSSStyleSheet + shadow.adoptedStyleSheets (replaceSync for { css }, replace
for { url }), and it is the only place in the ecosystem that touches adoptedStyleSheets or
carries a <style>-tag fallback for engines without constructable stylesheets. The caller
states the mode explicitly; nothing sniffs content.
:host; let the window size the element.icon.svg.attach_styles for every element.<style> or <link> tag in an element.!important aimed at a host page.attach_styles({ url }) at a URL without a version or hash in its path.