Skip to content

Commit 711e2eb

Browse files
serpentbladeclaude
andcommitted
fix(16): solid hoist <style> to document.head via __rozieInjectStyle
ITEM-1-RESIDUAL closure. Closes the LAST gated cell in the VR matrix — ThemedButtonConsumer · solid now passes byte-identical. Full matrix: 254 passed / 0 skipped / 0 failed (was 1 skipped after Item 3 + the two residuals). Root cause (caught visually by Dan — bold font missing in the Solid render). Solid had no CSS Modules pipeline, so emitStyle inlined each component's scoped CSS as a sibling `<style>` JSX element in the rendered tree. When a consumer composed a wrapper, the wrapper's `<style>` rendered AFTER the consumer's (one wrapper `<style>` per wrapper INSTANCE, all positioned as children of the consumer's container). Source-order made the wrapper's same-specificity rules win — the `.btn { font: inherit }` shorthand reset font-weight back to "normal", wiping the consumer's `.extra-variant { font-weight: 600 }` override. Visible symptom: 4 buttons rendered with correct colors but WITHOUT bold text, 454×52 vs 474×52 baseline (3 108 px diff). Every other target hoists styles to `<head>` once per component class (Vue `<style scoped>` via SFC compiler, React CSS Modules, Svelte `:global { }` Item-2 path, Angular `ViewEncapsulation.Emulated`, Lit `static styles = css\`\``). Solid was the lone target emitting inline `<style>` siblings. Fix. Switch Solid emit from inline `<style>` JSX to a module-top `__rozieInjectStyle('<name>-<hash>', \`<css>\`);` side-effect statement that runs ONCE at module-load time. Wrapper modules are loaded BEFORE consumer modules (consumer's `import X from '...'` resolves wrapper first), so wrapper styles land in `<head>` first and consumer styles second — restoring the source-order cascade that lets consumer rules win same-specificity races. Runtime helper is HMR-safe: a module-scoped `Map<key, HTMLStyleElement>` deduplicates by cache key (per-component scope hash); when an already- injected key receives different CSS text, the existing `<style>` element's `textContent` is updated in place rather than appending a duplicate. SSR is a no-op (`typeof document === 'undefined'` guard); Rozie's v1 Solid scope is client-side rendering. Files: - packages/runtime/solid/src/injectStyle.ts (NEW) — the runtime helper, with `Map<key, HTMLStyleElement>` cache + textContent-refresh on key-collision (HMR-safe) - packages/runtime/solid/src/index.ts — export `__rozieInjectStyle` - packages/targets/solid/src/emit/emitStyle.ts — signature gains `componentName` arg; result shape switches from `styleJsx` to `injectStatement` + `needsInjectHelper` flag; both scoped + :root sections fold into a single injection (separated by newline) keyed by `'<name>-<hash>'` - packages/targets/solid/src/emit/shell.ts — `ShellParts` field renamed `styleJsx` → `styleInjectStatement`; spliced at module top in both code paths (moduleParts + ms.append); inline `<style>` fragment-wrap removed from the JSX return - packages/targets/solid/src/emitSolid.ts — threads new field; registers `__rozieInjectStyle` runtime import when present - packages/targets/solid/src/rewrite/collectSolidImports.ts — adds `'__rozieInjectStyle'` to the RuntimeSolidImport enum - 8 target-solid snapshot fixtures + 23 dist-parity .solid.tsx + 6 regressions .solid.tsx — reblessed (uniform shape change: inline `<style>` removed from JSX, `__rozieInjectStyle(...)` added to module preamble + import added to the `@rozie/runtime-solid` line) - tests/visual-regression/specs/matrix.spec.ts — PHASE_14_1_FOLLOWUP set now EMPTY; closure comment captures the full three-stage Solid fix path Verification: - turbo run typecheck --force --continue: 47/47 green - turbo run test --continue: 46/46 green (target-solid 188/188, dist-parity 512/512, regressions 53/53) - solid-vite test:e2e: 6/6 green INCLUDING the `HMR preserves Counter state across a <style> edit (QA-04)` test — confirms the Map-based cache + textContent-refresh handles HMR correctly - tools/ci-repro/vr.sh -g ThemedButtonConsumer.*solid: cell passes byte-identical (was 3 108 px diff) - tools/ci-repro/vr.sh (full matrix): 254 passed / 0 skipped / 0 failed — every cell green; PHASE_14_1_FOLLOWUP fully closed The full pre-Phase-16 cleanup arc: - Pre-session: 229 passed / 25 skipped (16 of those skips were the five PHASE_14_1_FOLLOWUP cells across solid+svelte+lit) - Post-cleanup: 254 passed / 0 skipped / 0 failed — 25 cells unlocked across 7 commits (Items 1/2/3 + Item-1-residual + Item-2-residual + Item-1-cascade closure + Item-2-PortalListStyled-revert sub-fix). Solid is now at full cross-target parity for the cross-SFC composition and consumer-style-override cases — matching Vue/React/Svelte/Angular/ Lit on every cell in the matrix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6c551d9 commit 711e2eb

43 files changed

Lines changed: 614 additions & 539 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/runtime/solid/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ export { parseInlineStyle, toStyleObjectKey } from './parseInlineStyle.js';
2828
export { normalizeAttrs, SOLID_ATTR_KEY_MAP } from './normalizeAttrs.js';
2929
export { normalizeListeners, SOLID_LISTENER_KEY_MAP } from './normalizeListeners.js';
3030
export { mergeListeners } from './mergeListeners.js';
31+
export { __rozieInjectStyle } from './injectStyle.js';
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* injectStyle — Solid runtime helper for per-component style head-injection.
3+
*
4+
* Why this exists. Solid has no CSS Modules pipeline analogous to Vite's
5+
* React CSS Modules, so Rozie's pre-pre-Phase-16 Solid emitter inlined each
6+
* component's scoped CSS as a sibling `<style>` JSX element in the rendered
7+
* tree. That worked for the in-isolation case but broke same-specificity
8+
* cascade when a consumer composed a wrapper: the wrapper's `<style>` got
9+
* rendered AFTER the consumer's `<style>` (one wrapper `<style>` per
10+
* wrapper INSTANCE, all positioned as children of the consumer's container),
11+
* so source-order made the wrapper's same-specificity rules win — wiping
12+
* the consumer's `.extra-variant { font-weight: 600 }` overrides in the
13+
* `ThemedButtonConsumer · solid` matrix VR cell (a `.btn { font: inherit }`
14+
* shorthand from the wrapper reset font-weight back to "normal").
15+
*
16+
* Fix. Hoist each component's `<style>` content to `document.head` ONCE per
17+
* component (via this helper), called as a module-top side effect when the
18+
* emitted Solid component module is first imported. Wrapper modules are
19+
* imported BEFORE consumer modules (consumer's `import X from '...'`
20+
* resolves wrapper first), so wrapper styles land in `<head>` first and
21+
* consumer styles second — restoring the source-order cascade the other
22+
* five targets achieve via their respective per-framework style pipelines
23+
* (Vue `<style scoped>`, React CSS Modules, Svelte `:global { }`,
24+
* Angular `ViewEncapsulation.Emulated`, Lit `static styles = css\`\``).
25+
*
26+
* Cache. A module-scoped `Map<key, HTMLStyleElement>` deduplicates by the
27+
* caller-supplied key (typically the per-component scope hash). HMR-safe:
28+
* when an already-injected key receives different CSS text, the existing
29+
* `<style>` element's `textContent` is updated in place rather than a new
30+
* element being appended — so Vite HMR style edits propagate immediately.
31+
*
32+
* SSR. `typeof document === 'undefined'` is a no-op. Rozie's v1 Solid scope
33+
* is client-side rendering; SSR will need its own server-side style stream
34+
* mechanism (Solid Start uses `useAssets()`) when added later.
35+
*
36+
* @public
37+
*/
38+
39+
const __ROZIE_INJECTED = new Map<string, HTMLStyleElement>();
40+
41+
export function __rozieInjectStyle(key: string, css: string): void {
42+
if (typeof document === 'undefined') return;
43+
const existing = __ROZIE_INJECTED.get(key);
44+
if (existing) {
45+
// HMR / re-injection path: refresh the textContent if the css string
46+
// changed (don't append a duplicate `<style>` element).
47+
if (existing.textContent !== css) existing.textContent = css;
48+
return;
49+
}
50+
const el = document.createElement('style');
51+
el.setAttribute('data-rozie-style', key);
52+
el.textContent = css;
53+
document.head.appendChild(el);
54+
__ROZIE_INJECTED.set(key, el);
55+
}

packages/targets/solid/src/__tests__/fixtures/Card.snap.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import type { JSX } from 'solid-js';
22
import { children, mergeProps, splitProps } from 'solid-js';
3+
import { __rozieInjectStyle } from '@rozie/runtime-solid';
34
import CardHeader from './CardHeader';
45

6+
__rozieInjectStyle('Card-a88c221e', `.card[data-rozie-s-a88c221e] { border: 1px solid #ddd; border-radius: 6px; overflow: hidden; background: #fff; }
7+
.card__body[data-rozie-s-a88c221e] { padding: 1rem; }`);
8+
59
interface CardProps {
610
title?: string;
711
onClose?: (...args: unknown[]) => unknown;
@@ -16,9 +20,6 @@ export default function Card(_props: CardProps): JSX.Element {
1620
const resolved = children(() => local.children);
1721

1822
return (
19-
<>
20-
<style>{`.card[data-rozie-s-a88c221e] { border: 1px solid #ddd; border-radius: 6px; overflow: hidden; background: #fff; }
21-
.card__body[data-rozie-s-a88c221e] { padding: 1rem; }`}</style>
2223
<>
2324
<article {...attrs} class={"card" + (((attrs as unknown as Record<string, unknown>).class as string | undefined) ? " " + ((attrs as unknown as Record<string, unknown>).class as string | undefined) : "")} data-rozie-s-a88c221e="">
2425
<CardHeader title={local.title} onClose={local.onClose} data-rozie-s-a88c221e="" />
@@ -27,6 +28,5 @@ export default function Card(_props: CardProps): JSX.Element {
2728
</div>
2829
</article>
2930
</>
30-
</>
3131
);
3232
}
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import type { JSX } from 'solid-js';
22
import { Show, mergeProps, splitProps } from 'solid-js';
3+
import { __rozieInjectStyle } from '@rozie/runtime-solid';
4+
5+
__rozieInjectStyle('CardHeader-f3e60f5a', `.card-header[data-rozie-s-f3e60f5a] { display: flex; justify-content: space-between; align-items: center; padding: 0.75rem 1rem; border-bottom: 1px solid #eee; }
6+
.card-header__title[data-rozie-s-f3e60f5a] { margin: 0; font-size: 1rem; font-weight: 600; }
7+
.card-header__close[data-rozie-s-f3e60f5a] { background: none; border: 0; cursor: pointer; font-size: 1.25rem; padding: 0; line-height: 1; }`);
38

49
interface CardHeaderProps {
510
title?: string;
@@ -11,15 +16,10 @@ export default function CardHeader(_props: CardHeaderProps): JSX.Element {
1116
const [local, attrs] = splitProps(_merged, ['title', 'onClose']);
1217

1318
return (
14-
<>
15-
<style>{`.card-header[data-rozie-s-f3e60f5a] { display: flex; justify-content: space-between; align-items: center; padding: 0.75rem 1rem; border-bottom: 1px solid #eee; }
16-
.card-header__title[data-rozie-s-f3e60f5a] { margin: 0; font-size: 1rem; font-weight: 600; }
17-
.card-header__close[data-rozie-s-f3e60f5a] { background: none; border: 0; cursor: pointer; font-size: 1.25rem; padding: 0; line-height: 1; }`}</style>
1819
<>
1920
<header {...attrs} class={"card-header" + (((attrs as unknown as Record<string, unknown>).class as string | undefined) ? " " + ((attrs as unknown as Record<string, unknown>).class as string | undefined) : "")} data-rozie-s-f3e60f5a="">
2021
<h3 class={"card-header__title"} data-rozie-s-f3e60f5a="">{local.title}</h3>
2122
{<Show when={local.onClose}><button class={"card-header__close"} onClick={($event) => { (local.onClose)?.($event); }} data-rozie-s-f3e60f5a="">×</button></Show>}</header>
2223
</>
23-
</>
2424
);
2525
}

packages/targets/solid/src/__tests__/fixtures/Counter.snap.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import type { JSX } from 'solid-js';
22
import { createMemo, createSignal, mergeProps, splitProps } from 'solid-js';
3-
import { createControllableSignal, mergeListeners } from '@rozie/runtime-solid';
3+
import { __rozieInjectStyle, createControllableSignal, mergeListeners } from '@rozie/runtime-solid';
4+
5+
__rozieInjectStyle('Counter-c72e01d0', `.counter[data-rozie-s-c72e01d0] { display: inline-flex; gap: 0.5rem; align-items: center; }
6+
.counter.hovering[data-rozie-s-c72e01d0] { background: rgba(0, 0, 0, 0.04); }
7+
.value[data-rozie-s-c72e01d0] { font-variant-numeric: tabular-nums; min-width: 3ch; text-align: center; }
8+
button[data-rozie-s-c72e01d0] { padding: 0.25rem 0.5rem; }
9+
button[data-rozie-s-c72e01d0]:disabled { opacity: 0.4; cursor: not-allowed; }`);
410

511
interface CounterProps {
612
value?: number;
@@ -29,19 +35,12 @@ export default function Counter(_props: CounterProps): JSX.Element {
2935
}
3036

3137
return (
32-
<>
33-
<style>{`.counter[data-rozie-s-c72e01d0] { display: inline-flex; gap: 0.5rem; align-items: center; }
34-
.counter.hovering[data-rozie-s-c72e01d0] { background: rgba(0, 0, 0, 0.04); }
35-
.value[data-rozie-s-c72e01d0] { font-variant-numeric: tabular-nums; min-width: 3ch; text-align: center; }
36-
button[data-rozie-s-c72e01d0] { padding: 0.25rem 0.5rem; }
37-
button[data-rozie-s-c72e01d0]:disabled { opacity: 0.4; cursor: not-allowed; }`}</style>
3838
<>
3939
<div classList={{ hovering: hovering() }} {...attrs} class={"counter" + (((attrs as unknown as Record<string, unknown>).class as string | undefined) ? " " + ((attrs as unknown as Record<string, unknown>).class as string | undefined) : "")} {...mergeListeners({ onMouseEnter: ($event) => { setHovering(true); }, onMouseLeave: ($event) => { setHovering(false); } }, attrs)} data-rozie-s-c72e01d0="">
4040
<button aria-label="Decrement" disabled={!canDecrement()} onClick={decrement} data-rozie-s-c72e01d0=""></button>
4141
<span class={"value"} data-rozie-s-c72e01d0="">{value()}</span>
4242
<button aria-label="Increment" disabled={!canIncrement()} onClick={increment} data-rozie-s-c72e01d0="">+</button>
4343
</div>
4444
</>
45-
</>
4645
);
4746
}

packages/targets/solid/src/__tests__/fixtures/Dropdown.snap.tsx

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
import type { JSX } from 'solid-js';
22
import { Show, children, createEffect, mergeProps, onCleanup, onMount, splitProps, untrack } from 'solid-js';
3-
import { createControllableSignal, createOutsideClick, createThrottledHandler } from '@rozie/runtime-solid';
3+
import { __rozieInjectStyle, createControllableSignal, createOutsideClick, createThrottledHandler } from '@rozie/runtime-solid';
4+
5+
__rozieInjectStyle('Dropdown-6d6bd882', `.dropdown[data-rozie-s-6d6bd882] { position: relative; display: inline-block; }
6+
.dropdown-panel[data-rozie-s-6d6bd882] {
7+
position: fixed;
8+
z-index: var(--rozie-dropdown-z, 1000);
9+
background: white;
10+
border: 1px solid rgba(0, 0, 0, 0.1);
11+
border-radius: 6px;
12+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
13+
}
14+
:root {
15+
--rozie-dropdown-z: 1000;
16+
}`);
417

518
interface TriggerSlotCtx { open: any; toggle: any; }
619

@@ -77,19 +90,6 @@ export default function Dropdown(_props: DropdownProps): JSX.Element {
7790
});
7891

7992
return (
80-
<>
81-
<style>{`.dropdown[data-rozie-s-6d6bd882] { position: relative; display: inline-block; }
82-
.dropdown-panel[data-rozie-s-6d6bd882] {
83-
position: fixed;
84-
z-index: var(--rozie-dropdown-z, 1000);
85-
background: white;
86-
border: 1px solid rgba(0, 0, 0, 0.1);
87-
border-radius: 6px;
88-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
89-
}`}</style>
90-
<style>{`:root {
91-
--rozie-dropdown-z: 1000;
92-
}`}</style>
9393
<>
9494
<div {...attrs} class={"dropdown" + (((attrs as unknown as Record<string, unknown>).class as string | undefined) ? " " + ((attrs as unknown as Record<string, unknown>).class as string | undefined) : "")} data-rozie-s-6d6bd882="">
9595
<div ref={(el) => { triggerElRef = el as HTMLElement; }} onClick={toggle} data-rozie-s-6d6bd882="">
@@ -100,6 +100,5 @@ export default function Dropdown(_props: DropdownProps): JSX.Element {
100100
{typeof local.children === 'function' ? (local.children as (s: any) => any)({ close }) : resolved()}
101101
</div></Show>}</div>
102102
</>
103-
</>
104103
);
105104
}

packages/targets/solid/src/__tests__/fixtures/Modal.snap.tsx

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,32 @@
11
import type { JSX } from 'solid-js';
22
import { Show, children, createEffect, mergeProps, onCleanup, onMount, splitProps } from 'solid-js';
3-
import { createControllableSignal } from '@rozie/runtime-solid';
3+
import { __rozieInjectStyle, createControllableSignal } from '@rozie/runtime-solid';
4+
5+
__rozieInjectStyle('Modal-fc45feb2', `.modal-backdrop[data-rozie-s-fc45feb2] {
6+
position: fixed; inset: 0;
7+
background: rgba(0, 0, 0, 0.4);
8+
display: flex; align-items: center; justify-content: center;
9+
z-index: var(--rozie-modal-z, 2000);
10+
}
11+
.modal-dialog[data-rozie-s-fc45feb2] {
12+
background: white;
13+
border-radius: 8px;
14+
min-width: 20rem;
15+
max-width: min(90vw, 40rem);
16+
max-height: 90vh;
17+
display: flex; flex-direction: column;
18+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
19+
outline: none;
20+
}
21+
header[data-rozie-s-fc45feb2], footer[data-rozie-s-fc45feb2] { padding: 1rem; display: flex; align-items: center; gap: 0.5rem; }
22+
header[data-rozie-s-fc45feb2] { border-bottom: 1px solid rgba(0, 0, 0, 0.08); }
23+
header[data-rozie-s-fc45feb2] h2[data-rozie-s-fc45feb2] { flex: 1; margin: 0; font-size: 1.1rem; }
24+
footer[data-rozie-s-fc45feb2] { border-top: 1px solid rgba(0, 0, 0, 0.08); justify-content: flex-end; }
25+
.modal-body[data-rozie-s-fc45feb2] { padding: 1rem; overflow: auto; }
26+
.close-btn[data-rozie-s-fc45feb2] { background: none; border: none; cursor: pointer; font-size: 1.5rem; line-height: 1; }
27+
:root {
28+
--rozie-modal-z: 2000;
29+
}`);
430

531
interface HeaderSlotCtx { close: any; }
632

@@ -70,32 +96,6 @@ export default function Modal(_props: ModalProps): JSX.Element {
7096
});
7197

7298
return (
73-
<>
74-
<style>{`.modal-backdrop[data-rozie-s-fc45feb2] {
75-
position: fixed; inset: 0;
76-
background: rgba(0, 0, 0, 0.4);
77-
display: flex; align-items: center; justify-content: center;
78-
z-index: var(--rozie-modal-z, 2000);
79-
}
80-
.modal-dialog[data-rozie-s-fc45feb2] {
81-
background: white;
82-
border-radius: 8px;
83-
min-width: 20rem;
84-
max-width: min(90vw, 40rem);
85-
max-height: 90vh;
86-
display: flex; flex-direction: column;
87-
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
88-
outline: none;
89-
}
90-
header[data-rozie-s-fc45feb2], footer[data-rozie-s-fc45feb2] { padding: 1rem; display: flex; align-items: center; gap: 0.5rem; }
91-
header[data-rozie-s-fc45feb2] { border-bottom: 1px solid rgba(0, 0, 0, 0.08); }
92-
header[data-rozie-s-fc45feb2] h2[data-rozie-s-fc45feb2] { flex: 1; margin: 0; font-size: 1.1rem; }
93-
footer[data-rozie-s-fc45feb2] { border-top: 1px solid rgba(0, 0, 0, 0.08); justify-content: flex-end; }
94-
.modal-body[data-rozie-s-fc45feb2] { padding: 1rem; overflow: auto; }
95-
.close-btn[data-rozie-s-fc45feb2] { background: none; border: none; cursor: pointer; font-size: 1.5rem; line-height: 1; }`}</style>
96-
<style>{`:root {
97-
--rozie-modal-z: 2000;
98-
}`}</style>
9999
<>
100100
{<Show when={open()}><div class={"modal-backdrop"} ref={(el) => { backdropElRef = el as HTMLElement; }} onClick={($event) => { if ($event.target !== $event.currentTarget) return; local.closeOnBackdrop && close(); }} data-rozie-s-fc45feb2="">
101101
<div ref={(el) => { dialogElRef = el as HTMLElement; }} class={"modal-dialog"} role="dialog" aria-modal="true" aria-label={local.title || undefined} tabIndex={-1} data-rozie-s-fc45feb2="">
@@ -110,6 +110,5 @@ export default function Modal(_props: ModalProps): JSX.Element {
110110
{(_props.footerSlot ?? _props.slots?.['footer'])?.({ close })}
111111
</footer></Show>}</div>
112112
</div></Show>}</>
113-
</>
114113
);
115114
}

packages/targets/solid/src/__tests__/fixtures/SearchInput.snap.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import type { JSX } from 'solid-js';
22
import { Show, createMemo, createSignal, mergeProps, onCleanup, onMount, splitProps } from 'solid-js';
3-
import { createDebouncedHandler } from '@rozie/runtime-solid';
3+
import { __rozieInjectStyle, createDebouncedHandler } from '@rozie/runtime-solid';
4+
5+
__rozieInjectStyle('SearchInput-8bbc4a60', `.search-input[data-rozie-s-8bbc4a60] { display: inline-flex; align-items: center; gap: 0.25rem; }
6+
input[data-rozie-s-8bbc4a60] { padding: 0.25rem 0.5rem; }
7+
.clear-btn[data-rozie-s-8bbc4a60] { background: none; border: none; cursor: pointer; font-size: 1.25rem; }
8+
.hint[data-rozie-s-8bbc4a60] { color: rgba(0, 0, 0, 0.4); font-size: 0.85em; }`);
49

510
interface SearchInputProps {
611
placeholder?: string;
@@ -41,11 +46,6 @@ export default function SearchInput(_props: SearchInputProps): JSX.Element {
4146
const _rozieDebouncedOnSearch = createDebouncedHandler(onSearch, 300);
4247

4348
return (
44-
<>
45-
<style>{`.search-input[data-rozie-s-8bbc4a60] { display: inline-flex; align-items: center; gap: 0.25rem; }
46-
input[data-rozie-s-8bbc4a60] { padding: 0.25rem 0.5rem; }
47-
.clear-btn[data-rozie-s-8bbc4a60] { background: none; border: none; cursor: pointer; font-size: 1.25rem; }
48-
.hint[data-rozie-s-8bbc4a60] { color: rgba(0, 0, 0, 0.4); font-size: 0.85em; }`}</style>
4949
<>
5050
<div {...attrs} class={"search-input" + (((attrs as unknown as Record<string, unknown>).class as string | undefined) ? " " + ((attrs as unknown as Record<string, unknown>).class as string | undefined) : "")} data-rozie-s-8bbc4a60="">
5151

@@ -55,6 +55,5 @@ export default function SearchInput(_props: SearchInputProps): JSX.Element {
5555
×
5656
</button></Show>}</div>
5757
</>
58-
</>
5958
);
6059
}

packages/targets/solid/src/__tests__/fixtures/TodoList.snap.tsx

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import type { JSX } from 'solid-js';
22
import { For, Show, children, createMemo, createSignal, mergeProps, splitProps } from 'solid-js';
3-
import { createControllableSignal } from '@rozie/runtime-solid';
3+
import { __rozieInjectStyle, createControllableSignal } from '@rozie/runtime-solid';
4+
5+
__rozieInjectStyle('TodoList-52bec3de', `.todo-list[data-rozie-s-52bec3de] { font-family: system-ui, sans-serif; }
6+
ul[data-rozie-s-52bec3de] { list-style: none; padding: 0; }
7+
li[data-rozie-s-52bec3de] { display: flex; align-items: center; gap: 0.5rem; padding: 0.25rem 0; }
8+
li.done[data-rozie-s-52bec3de] span[data-rozie-s-52bec3de] { text-decoration: line-through; opacity: 0.5; }
9+
.empty[data-rozie-s-52bec3de] { color: rgba(0, 0, 0, 0.4); font-style: italic; }
10+
form[data-rozie-s-52bec3de] { display: flex; gap: 0.25rem; margin-block: 0.5rem; }`);
411

512
interface HeaderSlotCtx { remaining: any; total: any; }
613

@@ -58,13 +65,6 @@ export default function TodoList(_props: TodoListProps): JSX.Element {
5865
}
5966

6067
return (
61-
<>
62-
<style>{`.todo-list[data-rozie-s-52bec3de] { font-family: system-ui, sans-serif; }
63-
ul[data-rozie-s-52bec3de] { list-style: none; padding: 0; }
64-
li[data-rozie-s-52bec3de] { display: flex; align-items: center; gap: 0.5rem; padding: 0.25rem 0; }
65-
li.done[data-rozie-s-52bec3de] span[data-rozie-s-52bec3de] { text-decoration: line-through; opacity: 0.5; }
66-
.empty[data-rozie-s-52bec3de] { color: rgba(0, 0, 0, 0.4); font-style: italic; }
67-
form[data-rozie-s-52bec3de] { display: flex; gap: 0.25rem; margin-block: 0.5rem; }`}</style>
6868
<>
6969
<div {...attrs} class={"todo-list" + (((attrs as unknown as Record<string, unknown>).class as string | undefined) ? " " + ((attrs as unknown as Record<string, unknown>).class as string | undefined) : "")} data-rozie-s-52bec3de="">
7070
<header data-rozie-s-52bec3de="">
@@ -85,6 +85,5 @@ export default function TodoList(_props: TodoListProps): JSX.Element {
8585
</li>}</For>
8686
</ul></Show>}</div>
8787
</>
88-
</>
8988
);
9089
}

packages/targets/solid/src/__tests__/fixtures/TreeNode.snap.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import type { JSX } from 'solid-js';
22
import { For, Show, mergeProps, splitProps } from 'solid-js';
3+
import { __rozieInjectStyle } from '@rozie/runtime-solid';
4+
5+
__rozieInjectStyle('TreeNode-a7176a6e', `.tree-node[data-rozie-s-a7176a6e] { font-family: system-ui; padding-left: 0.5rem; }
6+
.tree-node__label[data-rozie-s-a7176a6e] { display: inline-block; }
7+
.tree-node__children[data-rozie-s-a7176a6e] { list-style: none; margin: 0.25rem 0 0 0; padding-left: 1rem; border-left: 1px dashed currentColor; }`);
38

49
interface TreeNodeProps {
510
node?: Record<string, any>;
@@ -14,10 +19,6 @@ export default function TreeNode(_props: TreeNodeProps): JSX.Element {
1419
const [local, attrs] = splitProps(_merged, ['node']);
1520

1621
return (
17-
<>
18-
<style>{`.tree-node[data-rozie-s-a7176a6e] { font-family: system-ui; padding-left: 0.5rem; }
19-
.tree-node__label[data-rozie-s-a7176a6e] { display: inline-block; }
20-
.tree-node__children[data-rozie-s-a7176a6e] { list-style: none; margin: 0.25rem 0 0 0; padding-left: 1rem; border-left: 1px dashed currentColor; }`}</style>
2122
<>
2223
<div {...attrs} class={"tree-node" + (((attrs as unknown as Record<string, unknown>).class as string | undefined) ? " " + ((attrs as unknown as Record<string, unknown>).class as string | undefined) : "")} data-rozie-s-a7176a6e="">
2324
<span class={"tree-node__label"} data-rozie-s-a7176a6e="">{local.node.label}</span>
@@ -27,6 +28,5 @@ export default function TreeNode(_props: TreeNodeProps): JSX.Element {
2728
</li>}</For>
2829
</ul></Show>}</div>
2930
</>
30-
</>
3131
);
3232
}

0 commit comments

Comments
 (0)