Rocket Conditional Pro

Rocket is a Pro feature, currently in beta.

Demo

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 #

1<div data-signals='{"step":0,"showDetails":false}'>
2    <conditional-panel
3        data-attr:step="$step"
4        data-attr:show-details="$showDetails"
5    ></conditional-panel>
6</div>

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})