@ohhwells/bridge 0.1.14 → 0.1.16-next.12

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
@@ -4,138 +4,266 @@ The OhhWells canvas editor bridge — a standalone npm package that enables inli
4
4
 
5
5
  ## What it does
6
6
 
7
- When a studio owner opens their site in the OhhWells dashboard canvas editor, the bridge:
7
+ When a studio owner opens their site in the OhhWells canvas editor, the bridge:
8
8
 
9
9
  - Connects the iframe (the live site) to the parent canvas editor via `postMessage`
10
10
  - Enables click-to-edit for text, images, and background images
11
11
  - Handles draft saving and content hydration
12
- - Provides state toggle UI for editing hidden content (hover states, form success/error views)
12
+ - Shows the "Add Section" insert line between sections in the canvas editor
13
+ - Provides state toggle UI for editing hidden content (hover states, form views)
13
14
  - Injects scoped styles that never leak into the host template
14
15
 
16
+ ---
17
+
15
18
  ## Installation
16
19
 
17
20
  ```bash
18
21
  npm install @ohhwells/bridge
19
22
  ```
20
23
 
21
- Import the styles once in your template's root layout:
24
+ ---
25
+
26
+ ## Template setup (required steps)
27
+
28
+ ### 1. Import styles
29
+
30
+ In your root layout file, import the bridge stylesheet **once**:
22
31
 
23
32
  ```ts
24
33
  import '@ohhwells/bridge/styles'
25
34
  ```
26
35
 
27
- ## Usage
36
+ ### 2. Add the loader surface
37
+
38
+ The loader is a full-screen spinner shown while the bridge fetches personalised content. It hides itself once content is ready. Add this **before** your main content in the `<body>`:
39
+
40
+ ```tsx
41
+ import { OHW_LOADER_STYLE, OhwLoaderSpinner } from '@ohhwells/bridge'
42
+
43
+ // Inside <body>:
44
+ <div
45
+ id="ohw-loader"
46
+ suppressHydrationWarning
47
+ style={{ ...OHW_LOADER_STYLE, display: 'none' }}
48
+ >
49
+ <OhwLoaderSpinner />
50
+ </div>
51
+
52
+ {/* Inline script — shows the loader immediately on the client before React hydrates */}
53
+ <script
54
+ dangerouslySetInnerHTML={{
55
+ __html: `(function(){try{
56
+ var p=location.hostname.split(".");
57
+ var fromHost=p.length>=3&&p[0]!=="www"?p[0]:"";
58
+ var fromQuery=new URLSearchParams(location.search).get("subdomain")||"";
59
+ if(!fromHost&&!fromQuery)return;
60
+ var e=document.getElementById("ohw-loader");
61
+ if(e)e.style.display="flex"
62
+ }catch(e){}})();`,
63
+ }}
64
+ />
65
+ ```
66
+
67
+ The inline script detects whether the page is being loaded under a subdomain (personalised content mode) and shows the loader before React has a chance to hydrate, preventing a flash of the default content.
28
68
 
29
- Add `OhhwellsBridge` to your template's root layout:
69
+ ### 3. Mount `OhhwellsBridge`
70
+
71
+ Add `<OhhwellsBridge />` inside a `<Suspense>` boundary in your root layout. It must be in `<Suspense>` because it calls `useSearchParams()` internally.
30
72
 
31
73
  ```tsx
74
+ import { Suspense } from 'react'
32
75
  import { OhhwellsBridge } from '@ohhwells/bridge'
33
76
 
34
77
  export default function RootLayout({ children }) {
35
78
  return (
36
- <html>
79
+ <html lang="en">
37
80
  <body>
81
+ {/* loader + inline script here (see step 2) */}
82
+
83
+ <Suspense>
84
+ <OhhwellsBridge />
85
+ </Suspense>
86
+
87
+ {/* rest of your layout */}
38
88
  {children}
39
- <OhhwellsBridge />
40
89
  </body>
41
90
  </html>
42
91
  )
43
92
  }
44
93
  ```
45
94
 
46
- The bridge activates automatically when the page is loaded inside the OhhWells canvas editor. It has no effect in production (live site) mode.
95
+ The bridge activates automatically when the page is loaded inside the OhhWells canvas editor. It does nothing in production (live site) mode.
47
96
 
48
- ## Template attributes
97
+ ### 4. Mark sections
49
98
 
50
- Mark elements as editable using `data-ohw-*` attributes. See [VIBECODER.md](https://github.com/TheFlowOps-Eng/rebound-template/blob/main/VIBECODER.md) for the full authoring guide.
99
+ Every top-level section on each page must have a unique `data-ohw-section` attribute. This is what the canvas editor uses to:
100
+ - Show the "Add Section" insert line between sections
101
+ - Persist widget insertions (saving which section a widget was inserted after)
51
102
 
52
- | Attribute | Value | Description |
53
- |---|---|---|
54
- | `data-ohw-key` | unique string | Identifier for storing/hydrating content |
55
- | `data-ohw-editable` | `text` \| `plain` \| `image` \| `bg-image` | Edit mode |
56
- | `data-ohw-editable-state` | `"hover,focus"` etc. | Declares available states on a container |
57
- | `data-ohw-state-view` | `"default"` \| `"success"` \| `"error"` \| `"hover"` | Wraps each state's content |
103
+ ```tsx
104
+ // Good — section element is the direct content root
105
+ <section data-ohw-section="hero">
106
+ ...
107
+ </section>
108
+
109
+ // Also fine — any element type works
110
+ <div data-ohw-section="testimonials">
111
+ ...
112
+ </div>
113
+ ```
58
114
 
59
- ## Design tokens
115
+ **Naming rules:**
116
+ - Use `kebab-case`
117
+ - Must be unique across the entire page
118
+ - Must be stable — if a section is renamed, any saved widget insertions referencing that name will break
60
119
 
61
- The package ships with the full OhhWells design token set, scoped to `[data-ohw-bridge]` so styles never leak into the host template.
120
+ **Example a page with multiple sections:**
62
121
 
63
- ### Semantic colors
122
+ ```tsx
123
+ export default function HomePage() {
124
+ return (
125
+ <>
126
+ <section data-ohw-section="hero">...</section>
127
+ <section data-ohw-section="lagree-intro">...</section>
128
+ <section data-ohw-section="classes-strip">...</section>
129
+ <section data-ohw-section="testimonials">...</section>
130
+ <section data-ohw-section="plan-form">...</section>
131
+ </>
132
+ )
133
+ }
134
+ ```
64
135
 
65
- All colors are defined as CSS variables on `[data-ohw-bridge-root]` and override-able per host:
136
+ ### 5. Mark editable elements
66
137
 
67
- | Token | Light | Dark |
68
- |---|---|---|
69
- | `background` | `#ffffff` | `#020617` |
70
- | `foreground` | `#020617` | `#f8fafc` |
71
- | `primary` | `#0f172a` | `#f8fafc` |
72
- | `primary-foreground` | `#f8fafc` | `#0f172a` |
73
- | `secondary` | `#f1f5f9` | `#1e293b` |
74
- | `muted` | `#f1f5f9` | `#1e293b` |
75
- | `muted-foreground` | `#64748b` | `#94a3b8` |
76
- | `accent` | `#f1f5f9` | `#1e293b` |
77
- | `border` | `#e2e8f0` | `#334155` |
78
- | `destructive` | `#dc2626` | `#7f1d1d` |
79
- | `success` | `#16a34a` | `#22c55e` |
138
+ Add `data-ohw-editable` and `data-ohw-key` to any element the studio owner should be able to edit:
80
139
 
81
- ### Typography
82
-
83
- Standard Tailwind scale with Figma-specified tracking:
84
-
85
- | Class | Size | Line height | Tracking |
86
- |---|---|---|---|
87
- | `text-9xl` | 128px | 1 | -0.025em |
88
- | `text-8xl` | 96px | 1 | -0.025em |
89
- | `text-7xl` | 72px | 1 | -0.025em |
90
- | `text-6xl` | 60px | 1 | -0.025em |
91
- | `text-5xl` | 48px | 1 | -0.025em |
92
- | `text-4xl` | 36px | 40px | -0.025em |
93
- | `text-3xl` | 30px | 36px | -0.025em |
94
- | `text-2xl` | 24px | 32px | -0.025em |
95
- | `text-xl` | 20px | 28px | -0.025em |
96
- | `text-lg` | 18px | 28px | 0 |
97
- | `text-base` | 16px | 24px | 0 |
98
- | `text-sm` | 14px | 20px | 0 |
99
- | `text-xs` | 12px | 16px | 0 |
140
+ ```tsx
141
+ {/* Editable rich text (bold, italic, etc.) */}
142
+ <h1
143
+ data-ohw-editable="text"
144
+ data-ohw-key="hero-heading"
145
+ >
146
+ Welcome to the studio
147
+ </h1>
148
+
149
+ {/* Editable plain text (no formatting) */}
150
+ <p
151
+ data-ohw-editable="plain"
152
+ data-ohw-key="hero-subtitle"
153
+ >
154
+ Book your first class
155
+ </p>
156
+
157
+ {/* Editable image */}
158
+ <img
159
+ data-ohw-editable="image"
160
+ data-ohw-key="hero-image"
161
+ src="/hero.jpg"
162
+ alt="Hero"
163
+ />
164
+
165
+ {/* Editable background image */}
166
+ <div
167
+ data-ohw-editable="bg-image"
168
+ data-ohw-key="hero-bg"
169
+ style={{ backgroundImage: 'url(/bg.jpg)' }}
170
+ />
171
+ ```
100
172
 
101
- ### Brand palette
173
+ **Key naming rules:**
174
+ - Must be globally unique across all pages
175
+ - Use `kebab-case`
176
+ - Must be stable — changing a key orphans any saved content for that element
102
177
 
103
- Re:Bound brand colors are available as named tokens: `bone`, `ivory`, `sand`, `linen`, `stone`, `clay`, `umber`, `umber-deep`, `espresso`, `clove`, `ink`, `ash`, `carbon`.
178
+ ---
104
179
 
105
- ## Dark mode
180
+ ## Editable states (advanced)
106
181
 
107
- Add the `dark` class to `[data-ohw-bridge-root]` to activate dark token overrides:
182
+ For elements with multiple display states (e.g. a contact form with default/success/error views), wrap each state in a `data-ohw-state-view` and mark the container with `data-ohw-editable-state`:
108
183
 
109
- ```html
110
- <div data-ohw-bridge-root class="dark">...</div>
184
+ ```tsx
185
+ <div
186
+ data-ohw-editable-state="default,success,error"
187
+ data-ohw-key="contact-form"
188
+ >
189
+ <div data-ohw-state-view="default">
190
+ {/* default form UI */}
191
+ </div>
192
+ <div data-ohw-state-view="success">
193
+ {/* success message */}
194
+ </div>
195
+ <div data-ohw-state-view="error">
196
+ {/* error message */}
197
+ </div>
198
+ </div>
111
199
  ```
112
200
 
113
- ## Development
201
+ The bridge shows a state toggle in the canvas editor to switch between states.
114
202
 
115
- ```bash
116
- # Install dependencies
117
- npm install
203
+ ---
204
+
205
+ ## Local development workflow
206
+
207
+ When iterating on the bridge package itself:
118
208
 
119
- # Build (TypeScript + CSS)
209
+ ```bash
210
+ # 1. Build the package
211
+ cd ohhwells-bridge
120
212
  npm run build
121
213
 
122
- # Watch mode
123
- npm run dev
214
+ # 2. Link it globally (one-time setup)
215
+ npm link
216
+
217
+ # 3. In your template repo, use the local build instead of the npm version
218
+ cd rebound-template
219
+ npm link @ohhwells/bridge
124
220
  ```
125
221
 
126
- Output is written to `dist/`:
127
- - `dist/index.js` — CJS
128
- - `dist/index.mjs` — ESM
129
- - `dist/index.d.ts` — TypeScript declarations
130
- - `dist/styles.css` — Scoped Tailwind CSS
222
+ After that, every `npm run build` in `ohhwells-bridge` is picked up by the template immediately (no re-link needed). To go back to the npm version:
223
+
224
+ ```bash
225
+ cd rebound-template
226
+ npm unlink @ohhwells/bridge
227
+ npm install
228
+ ```
229
+
230
+ ---
131
231
 
132
232
  ## Publishing
133
233
 
234
+ The package publishes automatically via GitHub Actions on push to `main` (production tag) or `staging` (next tag). To publish manually:
235
+
134
236
  ```bash
135
237
  npm run build
136
238
  npm publish --access public
137
239
  ```
138
240
 
241
+ ---
242
+
243
+ ## Design tokens
244
+
245
+ The package ships the full OhhWells design token set, scoped to `[data-ohw-bridge-root]` so styles never leak into the host template.
246
+
247
+ ### Semantic colors (CSS variables on `[data-ohw-bridge-root]`)
248
+
249
+ | Token | Light | Dark |
250
+ |---|---|---|
251
+ | `primary` | `#0f172a` | `#f8fafc` |
252
+ | `primary-foreground` | `#f8fafc` | `#0f172a` |
253
+ | `background` | `#ffffff` | `#020617` |
254
+ | `foreground` | `#020617` | `#f8fafc` |
255
+ | `muted` | `#f1f5f9` | `#1e293b` |
256
+ | `muted-foreground` | `#64748b` | `#94a3b8` |
257
+ | `border` | `#e2e8f0` | `#334155` |
258
+ | `destructive` | `#dc2626` | `#7f1d1d` |
259
+ | `success` | `#16a34a` | `#22c55e` |
260
+
261
+ ### Brand palette
262
+
263
+ Re:Bound brand colors: `bone`, `ivory`, `sand`, `linen`, `stone`, `clay`, `umber`, `umber-deep`, `espresso`, `clove`, `ink`, `ash`, `carbon`.
264
+
265
+ ---
266
+
139
267
  ## License
140
268
 
141
269
  MIT