Rocket Conditional Pro
Rocket is a Pro feature, currently in beta.
Explanation #
This version keeps the demo state on the page. Datastar controls drive the step and show-details props, and the Rocket component stays purely prop-driven.
This example also shows the difference between Rocket conditionals and data-show. The conditional branches unmount and remount as the state changes, while the details panel stays mounted and only toggles visibility.
Usage Example #
Rocket Component #
1import { rocket } from 'datastar'
2
3rocket('conditional-panel', {
4 props: ({ number, bool }) => ({
5 step: number.step(1).min(0).max(2),
6 showDetails: bool,
7 }),
8 render: ({ html, props: { step, showDetails } }) => html`
9 <section>
10 <style>
11 :host {
12 display: block;
13 --_outline-width: var(--size-1);
14 --_shadow-width: var(--size-px-1);
15 --_dark-color: var(--gray-3);
16 --_shadow-color: var(--gray-4);
17 --_color: var(--gray-5);
18 --_active-color: var(--gray-6);
19 --_text-color: var(--gray-12);
20 }
21
22 section {
23 display: grid;
24 gap: 0.75rem;
25 }
26
27 header {
28 display: flex;
29 flex-wrap: wrap;
30 align-items: center;
31 justify-content: space-between;
32 gap: 0.75rem;
33 }
34
35 button {
36 display: inline-flex;
37 position: relative;
38 align-self: flex-start;
39 align-items: center;
40 justify-content: center;
41 cursor: pointer;
42 font-size: var(--font-size-2);
43 font-family: var(--font-pixel);
44 -webkit-font-smoothing: none;
45 -moz-osx-font-smoothing: auto;
46 text-transform: uppercase;
47 padding: var(--size-2) var(--size-4) calc(var(--size-2) + var(--_shadow-width));
48 border: 0;
49 background-color: var(--_color);
50 color: var(--_text-color);
51 box-shadow: inset calc(-1 * var(--_shadow-width)) calc(-1 * var(--_shadow-width)) 0 0 var(--_shadow-color);
52 }
53
54 button:hover:not(:disabled, [aria-disabled='true']) {
55 color: var(--_text-color);
56 background-color: var(--_active-color);
57 }
58
59 button:active:not(:disabled, [aria-disabled='true']) {
60 box-shadow: inset var(--_shadow-width) var(--_shadow-width) 0 0 var(--_shadow-color);
61 padding: calc(var(--size-2) + var(--_shadow-width)) calc(var(--size-4) - var(--_shadow-width)) var(--size-2) calc(var(--size-4) + var(--_shadow-width));
62 }
63
64 button:focus-visible {
65 outline: 2px solid var(--blue-6);
66 outline-offset: 2px;
67 }
68
69 button::before,
70 button::after {
71 content: '';
72 position: absolute;
73 width: 100%;
74 height: 100%;
75 box-sizing: content-box;
76 }
77
78 button::before {
79 top: calc(-1 * var(--_outline-width));
80 left: 0;
81 border-top: var(--_outline-width) var(--_dark-color) solid;
82 border-bottom: var(--_outline-width) var(--_dark-color) solid;
83 }
84
85 button::after {
86 top: 0;
87 left: calc(-1 * var(--_outline-width));
88 border-left: var(--_outline-width) var(--_dark-color) solid;
89 border-right: var(--_outline-width) var(--_dark-color) solid;
90 }
91
92 [data-branch] {
93 padding: 0.9rem 1rem;
94 border-radius: 0.75rem;
95 border: 1px solid var(--gray-5);
96 background: var(--gray-1);
97 color: var(--gray-12);
98
99 strong,
100 p {
101 color: inherit;
102 }
103
104 p {
105 margin: 0.35rem 0 0;
106 }
107 }
108
109 [data-branch='loading'] {
110 border-color: var(--amber-6);
111 background: var(--amber-1);
112 color: var(--amber-12);
113 }
114
115 [data-branch='ready'] {
116 border-color: var(--green-6);
117 background: var(--green-1);
118 color: var(--green-12);
119 }
120
121 aside {
122 margin-top: 0.75rem;
123 padding: 0.75rem 1rem;
124 border-radius: 0.75rem;
125 background: var(--green-2);
126 color: var(--slate-12);
127 border: 1px solid var(--green-6);
128 line-height: 1.5;
129
130 code {
131 color: var(--green-11);
132 background: var(--green-3);
133 padding: 0.1rem 0.35rem;
134 border-radius: 0.35rem;
135 }
136 }
137
138 .callout {
139 margin-top: 0.75rem;
140 }
141 </style>
142
143 <header>
144 <strong>${step === 0 ? 'State: Idle' : step === 1 ? 'State: Loading' : 'State: Ready'}</strong>
145 </header>
146
147 <div>
148 ${step === 0
149 ? html`
150 <div data-branch="idle">
151 <strong>Idle</strong>
152 <p>
153 This branch exists only while the condition is true. Switching away
154 unmounts it completely.
155 </p>
156 </div>
157 `
158 : step === 1
159 ? html`
160 <div data-branch="loading">
161 <strong>Loading</strong>
162 <p>
163 The component can stay prop-driven while still swapping whole
164 branches in and out of the DOM.
165 </p>
166 </div>
167 `
168 : html`
169 <div data-branch="ready">
170 <strong>Ready</strong>
171 <p>
172 The fallback branch mounts fresh after earlier branches unmount.
173 </p>
174 </div>
175 `}
176
177 <div class="callout success">
178 <strong>data-show comparison</strong>
179 <p>
180 This box stays mounted and only toggles visibility, unlike the
181 conditional template branches above.
182 </p>
183 <aside data-show="${showDetails}">
184 <strong>Still mounted</strong>
185 <p>
186 <code>data-show</code> only changes visibility. The DOM node stays
187 in place, so its state, bindings, and event handlers remain intact.
188 </p>
189 </aside>
190 </div>
191 </div>
192 </section>
193 `,
194})