@cossackframework/renderer 0.7.1 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -129,6 +129,108 @@ import { unsafeHTML } from '@cossackframework/renderer';
129
129
  html`<div>${unsafeHTML('<script>...</script>')}</div>`
130
130
  ```
131
131
 
132
+ ### SVG Fragments
133
+
134
+ Use `svg` for fragments whose root nodes must be created in the SVG namespace.
135
+ SVG results can be nested, rendered in arrays, and used inside an HTML `<svg>`
136
+ element. The renderer switches back to the HTML namespace inside
137
+ `<foreignObject>`.
138
+
139
+ ```typescript
140
+ import { html, svg } from '@cossackframework/renderer';
141
+
142
+ const dot = (x: number, color: string) => svg`
143
+ <circle cx="${x}" cy="12" r="6" fill="${color}"></circle>
144
+ `;
145
+
146
+ html`<svg viewBox="0 0 100 24">${[dot(12, 'red'), dot(32, 'blue')]}</svg>`
147
+ ```
148
+
149
+ SVG and HTML templates serialize the same way during SSR. Namespace selection
150
+ is a client DOM concern and is preserved during hydration.
151
+
152
+ ### Rendering Nothing
153
+
154
+ `nothing` removes the value according to its binding context. An empty string
155
+ also renders no child node, but remains an ordinary value in attributes.
156
+
157
+ ```typescript
158
+ import { html, nothing } from '@cossackframework/renderer';
159
+
160
+ html`
161
+ <p>${showMessage ? message : nothing}</p>
162
+ <a title="prefix-${hasTitle ? title : nothing}">link</a>
163
+ <input .value=${hasValue ? value : nothing}>
164
+ <button ?disabled=${busy ? true : nothing}>Save</button>
165
+ <button @click=${enabled ? this.save : nothing}>Save</button>
166
+ <div ...=${enabled ? attributes : nothing}></div>
167
+ `
168
+ ```
169
+
170
+ In child expressions, `nothing` clears managed nodes. In normal attributes it
171
+ removes the whole attribute, including an attribute with several expressions.
172
+ Property bindings receive `undefined`, boolean attributes are removed, event
173
+ handlers are disabled, and a spread set to `nothing` removes its previously
174
+ managed values. SSR, hydration, and later updates use the same rules.
175
+
176
+ ## Component Styles
177
+
178
+ Declare Light DOM component styles with `static styles`. The `css` tag accepts
179
+ only numbers and nested `CSSResult` values in interpolations. Raw values require
180
+ the explicit `unsafeCSS` trust boundary.
181
+
182
+ ```typescript
183
+ import {
184
+ CossackElement,
185
+ css,
186
+ html,
187
+ unsafeCSS,
188
+ } from '@cossackframework/renderer';
189
+
190
+ const gap = 12;
191
+ const shared = css`.label { font-weight: 600; }`;
192
+ const reviewedThemeColor = unsafeCSS('rebeccapurple');
193
+
194
+ class Notice extends CossackElement {
195
+ static styles = [
196
+ shared,
197
+ css`
198
+ .notice { display: flex; gap: ${gap}px; color: ${reviewedThemeColor}; }
199
+ @media (width < 40rem) { .notice { display: block; } }
200
+ @keyframes enter { from { opacity: 0; } }
201
+ .notice { animation: enter 150ms; }
202
+ `,
203
+ ];
204
+
205
+ render() {
206
+ return html`<div class="notice"><span class="label">Ready</span></div>`;
207
+ }
208
+ }
209
+ ```
210
+
211
+ Style arrays may be nested. They are flattened in declaration order, and the
212
+ last occurrence of the same `CSSResult` wins. A subclass can extend inherited
213
+ styles explicitly:
214
+
215
+ ```typescript
216
+ class EmphasizedNotice extends Notice {
217
+ static styles = [Notice.styles, css`.notice { border: 2px solid currentColor; }`];
218
+ }
219
+ ```
220
+
221
+ Cossack scopes selectors by adding deterministic `data-cossack-scope`
222
+ attributes to elements created by that component and emits one managed style
223
+ element per component instance. Conditional at-rules and functional selectors
224
+ are scoped recursively; keyframe names and matching animation declarations are
225
+ rewritten. `:host`, `:host-context`, and `::slotted` are rejected because
226
+ Cossack uses Light DOM rather than Shadow DOM.
227
+
228
+ Scoping follows template ownership. Nested components use their own scope, while
229
+ projected templates retain the scope of the component that created them. This
230
+ is attribute-based isolation, not a Shadow DOM security boundary: inherited CSS
231
+ properties still inherit normally. `unsafeHTML` and manually supplied DOM nodes
232
+ are explicit escape hatches and are not guaranteed to receive scope attributes.
233
+
132
234
  ## Using Components in Templates
133
235
 
134
236
  Use the `component` helper function to include child components in your templates.
@@ -441,4 +543,4 @@ class Card extends CossackElement {
441
543
  `;
442
544
  }
443
545
  }
444
- ```
546
+ ```
@@ -4,5 +4,9 @@ export interface ComponentResult {
4
4
  clazz: new () => CossackElement;
5
5
  props: Record<string, unknown>;
6
6
  children: unknown;
7
+ /** Rendering owner captured when component() is evaluated. */
8
+ parent?: CossackElement | null;
9
+ /** Framework-private scope captured from the rendering owner. */
10
+ serviceScope?: unknown;
7
11
  }
8
12
  export declare const isComponentResult: (value: unknown) => value is ComponentResult;
@@ -1,5 +1,6 @@
1
1
  import { TemplateResult } from './cossack-html';
2
2
  import { Context } from './context';
3
+ import type { CSSResultGroup } from './css';
3
4
  export type PropertyDeclaration = {
4
5
  type?: unknown;
5
6
  reflect?: boolean;
@@ -28,6 +29,7 @@ export declare class CossackElement implements ReactiveControllerHost {
28
29
  static properties: PropertyDeclarations;
29
30
  static readonly _isCossackElement = true;
30
31
  static components: Record<string, typeof CossackElement>;
32
+ static styles?: CSSResultGroup;
31
33
  children: unknown;
32
34
  props: Record<string, unknown>;
33
35
  __parent: CossackElement | null;
@@ -56,6 +58,14 @@ export declare class CossackElement implements ReactiveControllerHost {
56
58
  private __notifyListeners;
57
59
  addRenderListener(listener: (template: TemplateResult | unknown | null) => void): void;
58
60
  removeRenderListener(listener: (template: TemplateResult | unknown | null) => void): void;
61
+ /** @internal Return the deterministic Light DOM style scope for this class. */
62
+ _getStyleScopeId(): string | undefined;
63
+ /**
64
+ * @internal Shared output finalizer used by standalone elements, nested
65
+ * component() rendering, and the core Cossack page/layout/app path.
66
+ */
67
+ _finalizeRenderOutput(value: TemplateResult | unknown | null): TemplateResult | unknown | null;
68
+ private _claimTemplateOwnership;
59
69
  mount(container: HTMLElement, hydrateFirst?: boolean): void;
60
70
  addEventListener(type: string, callback: EventListenerOrEventListenerObject | null, _options?: boolean | AddEventListenerOptions): void;
61
71
  removeEventListener(type: string, callback: EventListenerOrEventListenerObject | null, _options?: boolean | EventListenerOptions): void;